web-dev-qa-db-ger.com

Verwenden Sie den Klassennamen als Stammschlüssel für die JSON Jackson-Serialisierung

Angenommen, ich habe ein Pojo:

import org.codehaus.jackson.map.*;

public class MyPojo {
    int id;
    public int getId()
    { return this.id; }

    public void setId(int id)
    { this.id = id; }

    public static void main(String[] args) throws Exception {
        MyPojo mp = new MyPojo();
        mp.setId(4);
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
        System.out.println(mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE));
        System.out.println(mapper.writeValueAsString(mp));
    }
}

Wenn ich mit dem Jackson ObjectMapper serialisiere, bekomme ich es einfach 

true
{"id":4}

aber ich möchte 

true
{"MyPojo":{"id":4}}

Ich habe alles gesucht, Jacksons Dokumentation ist wirklich unorganisiert und meistens nicht mehr aktuell. 

26
DanInDC

Durch Hinzufügen der Jackson-Annotation @JsonTypeInfo auf Klassenebene können Sie die erwartete Ausgabe erhalten. Ich habe gerade keine Änderungen in Ihrer Klasse hinzugefügt.

package com.test.jackson;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class MyPojo {
    // Remain same as you have
}

Ausgabe:

{
    "MyPojo": {
        "id": 4
    }
}
28
Arun Prakash

Ich benutze nicht Jackson, aber beim Suchen habe ich diese Konfiguration gefunden, die scheinbar Ihren Wünschen entspricht: WRAP_ROOT_VALUE

Funktion, die aktiviert werden kann, um einen Root-Wert zu erstellen (normalerweise JSON-Objekt, kann jedoch ein beliebiger Typ sein), das in ein einzelnes Eigenschafts-JSON-Objekt eingeschlossen wird, wobei key als "root-Name" gilt, wie durch Annotation Introspector (insbesondere für JAXB, das @XmlRootElement verwendet) bestimmt .name) oder Fallback (nicht qualifizierter Klassenname). Diese Funktion ist hauptsächlich für die Kompatibilität mit JAXB gedacht.

Die Standardeinstellung ist "false" (root) Wert wird nicht verpackt.

Damit Sie Mapper konfigurieren können:

objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);

Ich hoffe es hilft dir ...

25
Aito

Unten ist ein Weg, um dies zu erreichen

Map<String, MyPojo> singletonMap = Collections.singletonMap("mypojo", mp);
System.out.println(mapper.writeValueAsString(singletonMap));

Ausgabe {"Mypojo": {"id": 4}}

Hier ist der Vorteil, dass wir den Namen des Wurzelschlüssels von json object mit on angeben können. Mit dem obigen Code wird mypojo der Root-Schlüssel. Dieser Ansatz ist am nützlichsten, wenn wir für die Iteration von Json-Objekten eine Java-Skriptvorlage wie Mustache.js verwenden 

12
Anish George

Um dies zu erreichen, müssen Sie die Annotation JsonTypeInfo für Ihre Klasse und insbesondere WRAPPER_OBJECT verwenden.

@JsonTypeName("foo")                                                                                         
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)

public class Bar(){
)
4
user2083529

Es gibt auch eine schöne Anmerkung dazu:

@JsonRootName(value = "my_pojo")
public class MyPojo{
  ...
}

wird generieren:

{
  "my_pojo" : {...}
}
3
JeanValjean

es gibt einen anderen Weg, den ich verwendet habe, und das hat für mich funktioniert. Ich arbeite mit einem Drittanbieterglas, daher habe ich keine Kontrolle über Anmerkungen. Ich musste also etwas durchschneiden.

Überschreiben: org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(SerializationConfig, BasicBeanDescription)

Fügen Sie Ihre Immobilie wie folgt hinzu

List<BeanPropertyWriter> props = super.findBeanProperties(config, beanDesc);
BeanPropertyWriter bpw = null;
try {
     Class cc = beanDesc.getType().getRawClass();
     Method m = cc.getMethod("getClass", null);
     bpw = new BeanPropertyWriter("$className", null, null, m, null,true, null);
} catch (SecurityException e) {
  // TODO
} catch (NoSuchMethodException e) {
  // TODO
}
props.add(bpw);
return props;

Auf diese Weise bekomme ich mehr Kontrolle und kann auch andere Filter verwenden.

1
shailendra

Wie wäre es mit einer möglichst einfachen Lösung? Verwenden Sie einfach eine Wrapper-Klasse wie:

class Wrapper {
   public MyPojo MyPojo;
}

und in Ihrem Code einwickeln/auspacken?

Darüber hinaus wäre es hilfreich zu wissen, warum Sie einen zusätzlichen json-Objekteintrag wie diesen wünschen. Ich weiß, dass dies von libs gemacht wird, die json über xml api emulieren (wegen der Impedanz zwischen xml und json, aufgrund der Umwandlung von xml in json), aber für reine Json-Lösungen ist dies normalerweise nicht erforderlich.

Erlauben Sie es Ihnen, herauszufinden, was der tatsächliche Typ ist? Wenn dies der Fall ist, könnten Sie möglicherweise polymorphe Typinformationen aktivieren, um Jackson automatisch damit umgehen zu lassen. (Für Details siehe 1.5 Versionshinweise , Eintrag für PTH,).

1
StaxMan

Ich wäre daran interessiert, die Lösung des OP dafür zu hören. Ich habe ähnliche Probleme, bei denen mein RESTful-Webdienst Objekte als XML oder JSON für Clients serialisiert. Die Javascript-Clients müssen den Wrapping-Typ kennen, damit sie analysiert werden können. Das Koppeln des Typs an ein URI-Muster ist keine Option.

Vielen Dank.

Edit: Ich habe bemerkt, dass Spring MappingJacksonJsonMarshaller beim Marshalling die Wrapping-Klasse hinzufügt. Ich ging den Code in Debug durch und stellte fest, dass Spring in einer HashMap ein einzelnes Schlüssel-Wert-Paar durchläuft, sodass der Schlüssel der Wrapping-Name ist und der Wert der Objekt. Also habe ich JacksonJaxbJsonProvider erweitert, die writeTo () -Methode überschrieben und Folgendes hinzugefügt:

HashMap<String, Object> map = new HashMap<String, Object>();
map.put(value.getClass().getSimpleName(), value);
super.writeTo(map, type, genericType, annotations, mediaType, httpHeaders,entityStream);

Es ist ein bisschen ein Hack, aber es funktioniert gut.

0
Tony

Ich habe durch Erfahrung festgestellt, dass es für alle JSON eine gute Idee ist, sowohl den Backend-Typ (als String) als auch den Komponententyp zu verwenden, der für das Rendern im Front-End verwendet wird (wenn etwas wie Angle oder Vue verwendet wird).

Die Begründung dafür ist, dass Sie verschiedene Typen mit einem einzigen Codesatz verarbeiten können. 

Wenn Sie beispielsweise den Namen der UI-Komponente in den Daten angeben, können Sie unter anderem einen Bildschirm mit einer Liste von untergeordneten Typen mit unterschiedlichen Typen anzeigen, wobei nur ein einziges Tag in der übergeordneten Vorlage verwendet wird.

  <component :is="child.componentType"/>.

Für Backend-Systeme und Web-Services: Ich bevorzuge die Verwendung einer einzigen Web-Service-Prozessorklasse, die Protokollierung, Überwachung und Ausnahmebehandlung für alle Web-Services bereitstellt, indem die entsprechende Prozessorklasse basierend auf der eingehenden Nutzlast nachgeschlagen wird. Dadurch sieht die Implementierung aller meiner Web-Services genau gleich aus (etwa 3 Zeilen Code), und ich bekomme eine detaillierte Ereignisprotokollierung während des gesamten Lebenszyklus des Anrufs, ohne dafür je Service-Code schreiben zu müssen.

Wenn der Typ den JSON umhüllt, ist er selbstdokumentierend. Wenn Sie nur die Eigenschaften sehen, haben Sie keine Ahnung, was Sie betrachten, bis Sie den entsprechenden Endpunkt gefunden haben.

Wenn Sie datengesteuerte Software schreiben möchten, ist es eine Grundvoraussetzung, dass Sie erkennen können, was Sie verarbeiten.

0
@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) 

Diese Anmerkung funktioniert perfekt, wie von Arun Prakash vorgeschlagen. Ich habe versucht, Json in dieser Form zu bekommen. 

{"Rowset":{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

aber so bekommen:

{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

Nun löste diese Anmerkung mein Problem.

0
Naushad Idrisi

verwenden Sie withRootName.

objectMapper.writer().withRootName(MyPojo.class.getName());
0
winry