Use el nombre de la clase como clave raíz para la serialización JSON Jackson

Supongamos que tengo un 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)); } } 

Cuando serializo usando el ObjectMapper de Jackson, acabo de obtener

 true {"id":4} 

pero yo quiero

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

He buscado por todas partes, la documentación de Jackson está realmente desorganizada y en su mayoría desactualizada.

Al agregar la anotación de jackson @JsonTypeInfo en el nivel de clase, puede obtener el resultado esperado. Acabo de agregar ningún cambio en tu clase.

 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 } 

salida:

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

No estoy usando jackson, pero al buscar encontré esta configuración que parece ser la que quieres: WRAP_ROOT_VALUE

Característica que se puede habilitar para hacer que el valor raíz (normalmente el objeto JSON pero puede ser de cualquier tipo) envuelto dentro de un objeto JSON de propiedad única, donde la clave es el “nombre raíz”, según lo determina el introspector de anotación (especialmente para JAXB que usa @XmlRootElement .name) o fallback (nombre de clase no calificado). La función está destinada principalmente a la compatibilidad con JAXB.

La configuración predeterminada es falsa, lo que significa que el valor raíz no está ajustado.

Para que pueda configurar mapper:

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

Espero que te ayude…

A continuación hay una forma de lograr esto

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

Salida {“mypojo”: {“id”: 4}}

Aquí la ventaja es que podemos dar nuestro nombre para la clave raíz del objeto json. Por el código anterior, mypojo será la clave raíz. Este enfoque será más útil cuando usamos la plantilla de script java como Mustache.js para la iteración de objetos json

También hay una buena anotación para esto:

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

Generará:

 { "my_pojo" : {...} } 

Para lograr esto, necesita utilizar la anotación JsonTypeInfo en su clase y, en particular, WRAPPER_OBJECT

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

¿Qué tal la solución más simple posible? solo use una clase de contenedor como:

 class Wrapper { public MyPojo MyPojo; } 

y envolviendo / desenvolver en su código?

Más allá de esto, ayudaría saber POR QUÉ le gustaría una entrada de objeto json adicional como esta? Sé que esto es hecho por libs que emulan json a través de xml api (debido a la impedancia entre xml y json, debido a la conversión de xml a json), pero para las soluciones json puras generalmente no es necesario.

¿Es para permitir que descubras qué tipo real es? Si es así, ¿tal vez podría considerar habilitar la información de tipo polimórfico, para permitir que Jackson lo maneje automáticamente? (ver 1.5 notas de la versión , entrada para PTH, para más detalles).

hay otra manera que utilicé y que funcionó para mí. Estoy trabajando con un jar de terceros, por lo que no tengo control para las anotaciones. Así que tuve que escribir algo de hack.

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

Agregue su propiedad de la siguiente manera

 List 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; 

De esta manera, tengo más control y también puedo hacer otros tipos de filtros.

Me interesaría escuchar la solución de OP para esto. Tengo problemas similares donde mi servicio web RESTful está serializando objetos como XML o JSON para clientes. Los clientes de Javascript necesitan saber el tipo de envoltura para poder analizarlo. Acoplamiento del tipo a un patrón de URI no es una opción.

Gracias.

Editar: Observé que Spring MappingJacksonJsonMarshaller agrega la clase de ajuste al ordenar, así que puse el código en depuración y noté que Spring pasa en un HashMap con un solo par de clave-valor de modo que la clave es el nombre de ajuste y el valor es el objeto. Entonces, extendí JacksonJaxbJsonProvider, anulé el método writeTo () y agregué lo siguiente:

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

Es un truco, pero funciona bien.

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

Esta anotación funciona perfectamente, como sugiere Arun Prakash. Estaba tratando de obtener json en esta forma.

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

pero obteniendo así:

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

Ahora esa anotación resolvió mi problema.

usar conRootName.

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

A través de la experiencia, he descubierto que es una buena idea que todos los JSON incluyan tanto el tipo de backend (como una cadena) como el tipo de componente utilizado para representarlo en la parte frontal (si se usa algo como angular o Vue).

La justificación para hacer esto es que puede procesar varios tipos con un solo conjunto de código.

En vue, por ejemplo, tener el nombre del componente UI en los datos le permite, entre otras cosas, tener una pantalla que represente una lista de niños de diferentes tipos utilizando solo una etiqueta en la plantilla primaria.

  . 

Para sistemas de fondo y servicios web: prefiero usar una única clase de procesador de servicio web que proporcione registro, auditoría y manejo de excepciones para todos los servicios web al buscar la clase de procesador adecuada en función de la carga útil entrante. Eso hace que la implementación de todos mis servicios web se vea exactamente igual (alrededor de 3 líneas de código) y obtengo un registro de eventos detallado a lo largo del ciclo de vida de la llamada sin escribir ningún código de servicio para hacerlo.

Tener el tipo que envuelve el JSON lo hace documentarse por sí mismo. Si todo lo que ve son las propiedades, no tiene idea de lo que está mirando hasta que encuentre el punto final correspondiente.

Si desea escribir software impulsado por datos, ser capaz de identificar lo que está procesando es un requisito básico.