xmlmapper jsonignore example deserializer deserialize custom java json parsing jersey jackson

java - jsonignore - jackson custom deserializer



Reglas para Jersey para analizar la deserialización del subtipo JSON/Jackson (1)

Pasé algún tiempo para resolver esto esta mañana. Un caso de uso interesante. Descubrí cómo hacerlo, pero tuve que cambiar tu json ligeramente. Esto no es estrictamente necesario, sin embargo, la conversión de tipo no fue parte de su pregunta, por lo que podemos hacer un seguimiento si es necesario :)

Tu json:

artur@pandaadb:~/tmp/test$ cat 1.json { "eventType": "1", "params": { "field1" : 10 } } artur@pandaadb:~/tmp/test$ cat 2.json { "eventType": "2", "params": { "field2" : "10" } }

Estoy usando esos 2 archivos para hacer la solicitud. Tenga en cuenta que cambié el evento tipoT ser una cadena en lugar de un número. Lo señalaré más tarde.

Tus objetos modelo:

public class Stats { @JsonProperty int eventType; public Params params; @JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.EXTERNAL_PROPERTY, property="eventType") @JsonSubTypes({ @Type(value = Param1.class, name = "1"), @Type(value = Param2.class, name = "2") }) public void setParams(Params params) { this.params = params; } }

Estoy usando JsonTypeInfo, esto es lo que hace:

JsonTypeInfo.Id.NAME

nombre de tipo lógico, en su caso es la propiedad "eventType"

JsonTypeInfo.As.EXTERNAL_PROPERTY

Significa que una propiedad externa se usa para el contexto de deserialización. Solo puede usar eso en la propiedad , no como una anotación de clase en Params. Es por eso que anotar el método setter en lugar de la clase de interfaz.

property="eventType"

Simplemente le dice a Jackson qué nombre de propiedad usar

Luego, en JsonSubTypes anoto las opciones que son posibles, en su caso 2:

@Type(value = Param1.class, name = "1")

esto le dice a jackson que use Param1.class en caso de que la propiedad eventType sea "1"

En consecuencia, lo mismo para PAram2.class y el valor de la propiedad es "2"

NOTA: Esta es la razón por la que cambié el json ligeramente. Las anotaciones de subtipo no pueden tomar un entero como propiedad. Ahora hay diferentes opciones que podría estar utilizando, por ejemplo, TypeConverters que convierten su propiedad integer en una cadena en tiempo de ejecución, y de esa manera puede mantener su json el mismo. Me salté ese paso, un google rápido te dará instrucciones sobre cómo hacerlo.

Ahora su modelo de parámetro se ve así:

public interface Params { public static class Param1 implements Params { @JsonProperty int field1; } public static class Param2 implements Params { @JsonProperty String field2; } }

Anoto las propiedades para que Jackson sepa deserializarlas.

NOTA : tuve un pequeño problema porque hay dos propiedades que para mis ojos cansados ​​y perezosos son las mismas:

JsonTypeInfo.As.EXTERNAL_PROPERTY JsonTypeInfo.As.EXISTING_PROPERTY

No puede usar EXISTENTE: D Esto literalmente tomó diez minutos en darse cuenta. De hecho, tenía ambas líneas arriba y continuaba comentando una, sin entender por qué en la tierra uno de ellos lanza una excepción mientras el otro funciona.

De todas formas.

Y finalmente la prueba:

artur@pandaadb:~/tmp/test$ curl -XPOST "localhost:8085/api/v2/test" -d @1.json -H "Accept: application/json" -H "Content-Type: application/json" io.gomedia.resource.Params$Param1 artur@pandaadb:~/tmp/test$ artur@pandaadb:~/tmp/test$ curl -XPOST "localhost:8085/api/v2/test" -d @2.json -H "Accept: application/json" -H "Content-Type: application/json" io.gomedia.resource.Params$Param2

Tenga en cuenta que el recurso está imprimiendo el nombre de la clase instanciada. Como puede ver, ambos json han sido deserializados en la clase de instancia correcta.

Espero que eso ayude :)

Artur

(Dato curioso # 2: en mi respuesta también utilicé EXISTING y no EXTERNAL y simplemente no lo vi. Puede que necesite pedirle a Jackson que cambie sus nombres por el bien de mi cordura)

EDITAR

Lo intenté, y Jackson es lo suficientemente inteligente como para convertir a tu JSON por ti. Por lo tanto, puede dejar el json tal como está, y simplemente tener la propiedad en su modelo como una cadena (como se demostró). Todo funciona bien

Sin embargo, para que esté completo, en caso de que quiera un convertidor (porque podría necesitarlo para convertir su modelo de cadena en un número entero para la serialización), este sería un convertidor de entero a cadena:

public class EventTypeConverter implements Converter<Integer, String>{ @Override public String convert(Integer value) { return String.valueOf(value); } @Override public JavaType getInputType(TypeFactory typeFactory) { return SimpleType.construct(Integer.class); } @Override public JavaType getOutputType(TypeFactory typeFactory) { return SimpleType.construct(String.class); } }

Puedes usarlo haciendo:

@JsonProperty @JsonDeserialize(converter=EventTypeConverter.class) String eventType;

Recibo JSONs de la manera:

@POST @Path("log") public Map<String, List<OperationResult>> log(Stats stats) { .. }

Los ejemplos de JSON:

{ "eventType": 1 "params": { "field1" : 10 } } { "eventType": 2 "params": { "field2" : "ten" } }

Tengo una estructura de clase (son generados por jsonschema2pojo, supongo que no importa):

interface Params; class Params1 implements Params{ public int field1; } class Params2 implements Params{ public String field2; } class Stats { public int eventType; public Params params; }

¿Cómo puedo hacer que Jersey analice los JSON para que si eventType = 1 entonces stats.params se convierta en una instancia de Params1 y en otro de Params2?