2010-03-12 14 views
20

Supponiamo che io sono un POJO:Usa nome di classe come la chiave principale per JSON Jackson serializzazione

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)); 
    } 
} 

Quando ho serializzare utilizzando la Jackson ObjectMapper, ottengo solo

true 
{"id":4} 

ma voglio

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

Ho cercato dappertutto, la documentazione di Jacksons è davvero disorganizzata e per lo più obsoleta.

risposta

24

non sto usando Jackson, ma la ricerca ho trovato questa configurazione che sembra essere ciò che si vuole: WRAP_ROOT_VALUE

Caratteristica che può essere attivata per rendere il valore root (di solito JSON oggetto, ma può essere di qualsiasi tipo) racchiuso all'interno di un oggetto JSON di proprietà singola, dove chiave come "nome radice", come determinato dall'introspector dell'annotazione (specialmente per JAXB che utilizza @ XmlRootElement.name) o fallback (nome classe non qualificato). La funzionalità è principalmente destinata alla compatibilità con JAXB.

L'impostazione predefinita è false, ovvero il valore radice non viene spostato.

In modo che è possibile configurare mapper:

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

Spero che ti aiuta ...

+5

benvenuti nel mondo stravagante di Jacksons documentazione delle caratteristiche che in realtà non funzionano. Grazie per l'aiuto però. – DanInDC

+0

FWIW, questa sarebbe l'implementazione prevista (ciò che è stato discusso come previsto). L'inclusione dell'enumerazione delle feature è sfortunata, dal momento che tutte le altre funzionalità sono implementate (AFAIK), e non ha certamente senso aggiungerle prima dell'implementazione. Se lo si desidera implementato, è possibile (oltre a contribuire all'implementazione) votare per esso in Jira e/o lobby per quello sulle mailing list. – StaxMan

+4

Vedere anche [JACKSON-630] (http://jira.codehaus.org/browse/JACKSON-630) che ha provocato l'annotazione '@ JsonRootName' aggiunta in Jackson 1.9 per la configurazione del nome radice. 'WRAP_ROOT_VALUE' deve essere ancora abilitato affinché questa annotazione abbia effetto. –

1

Come su più semplice possibile soluzione; basta usare una classe wrapper come:

class Wrapper { 
    public MyPojo MyPojo; 
} 

e wrapping/unwrapping nel codice?

Oltre a questo, sarebbe utile sapere PERCHÉ desideri aggiungere una voce aggiuntiva a un oggetto json come questa? So che questo è fatto dalle librerie che emuliano json via xml api (a causa dell'impedenza tra xml e json, a causa della conversione da xml a json), ma per le soluzioni json pure di solito non è necessario.

È per consentire di capire qual è il tipo effettivo? In tal caso, forse potresti prendere in considerazione le informazioni del tipo polimorfico abilitato, per consentire a Jackson di gestirlo automaticamente? (vedi 1.5 release notes, voce per PTH, per i dettagli).

+0

Perché: Perché sto prendendo il JSON serializzato, trasformandolo in un oggetto JSONObject e quindi aggiungendo quel JSONObject a un JSONObject principale. Il mio oggetto radice è un oggetto di trasporto che contiene alcuni tipi diversi di oggetti. Ho bisogno di conoscere il tipo dall'altra parte quando li deserializzo. Ho un sistema funzionante che non mi dà fastidio. Grazie per la risposta. – DanInDC

+0

Ah ok. Come ho detto, Jackson è in grado di capire da solo il testo senza avvolgere, ma forse non è il caso di altre librerie. Felice di sapere che le cose stanno funzionando in entrambi i modi. – StaxMan

0

Sarei interessato a sentire la soluzione dell'OP per questo. Sto riscontrando problemi simili in cui il mio servizio web RESTful sta serializzando oggetti come XML o JSON per i client. I client Javascript devono conoscere il tipo di wrapping in modo da poterlo analizzare. Accoppiare il tipo a un modello URI non è un'opzione.

Grazie.

Edit: ho notato che la primavera MappingJacksonJsonMarshaller aggiunge la classe di avvolgimento quando smistamento, così ho fatto un passo attraverso il codice di debug e ho notato che la primavera passa in un HashMap con una sola coppia chiave-valore tale che la chiave è il nome confezionamento e il valore è l'oggetto. Così, ho esteso JacksonJaxbJsonProvider, l'override del metodo WriteTo() e aggiunto il seguente:

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

E 'un po' di un hack, ma funziona bene.

+0

Potresti approfondire la tua modifica? Non penso che MappingJacksonJsonMarshaller esista ma sembra una soluzione a un problema che sto riscontrando ... o almeno uno che vale la pena provare. – AHungerArtist

+0

Per REST, dovresti farlo tramite conneg. Usa l'intestazione "Accetta". – HDave

1

c'è un altro modo che ho usato e che ha funzionato per me. Sto lavorando con un jar di terze parti, quindi non ho controllo per le annotazioni. Quindi ho dovuto scrivere attraverso un po 'di hacking.

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

Aggiungere la vostra proprietà, come di seguito

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; 

In questo modo ottengo un maggiore controllo e posso fare altri tipi di filtri troppo.

10

Qui di seguito è un modo per raggiungere questo

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

uscita { "mypojo": { "id": 4}}

Qui il vantaggio è che possiamo dare il nostro sul nome della chiave radice dell'oggetto json. Con il codice sopra, mypojo sarà la chiave principale. Questo approccio sarà molto utile quando usiamo template di script java come Mustache.js per l'iterazione di oggetti json

21

Aggiungendo l'annotazione di jackson @JsonTypeInfo a livello di classe è possibile ottenere l'output previsto. Ho appena aggiunto nessun cambiamento nella tua classe.

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 
} 

uscita:

{ 
    "MyPojo": { 
     "id": 4 
    } 
} 
+2

Perfetto. Ha funzionato per me C'è un modo per cambiare il descrittore per non essere il nome della classe? Come 'mypojo' invece di' MyPojo'? –

+0

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

3

C'è anche un bel annotazioni per questo:

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

genererà:

{ 
    "my_pojo" : {...} 
} 
0
@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) 

Questa annotazione funziona perfettamente, come suggerito da Arun Prakash. Stavo cercando di ottenere json in questa forma.

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

ma ottenendo in questo modo:

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

Ora che l'annotazione risolto il mio problema.

0

utilizzare conRootName.

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

per raggiungere questo obiettivo è necessario utilizzare il JsonTypeInfo annotazioni sulla vostra classe e, in particolare, WRAPPER_OBJECT

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

public class Bar(){ 
)