Sé cómo usar un serializador personalizado en Jackson (mediante la ampliación de JsonSerializer), pero quiero que el defecto serializador para el trabajo de todos los campos, excepto para 1 solo campo, que quiero reemplazar el uso de la costumbre serializador.

Anotaciones no son una opción, porque soy serializar una clase generada (de segunda mano).

¿Cómo puedo especificar los campos que va a ser reemplazado cuando se escribe una costumbre jackson serializador?

Actualización:

Aquí está la clase que desea serializar:

class Student {
    int age;
    String firstName;
    String lastName;
    double average;
    int numSubjects

    //.. more such properties ...
}

La clase anterior tiene muchas properies, la mayoría de los cuales utilizan tipos nativos. Sólo quiero reemplazar un par de propiedades en el serializador personalizado y vamos a Jackson lidiar con el resto, como de costumbre. Por ejemplo, sólo quiero convertir la «edad» de campo a una aduana de salida.

InformationsquelleAutor jeffreyveon | 2013-03-13

5 Comentarios

  1. 6

    A los que me enfrentaba al mismo problema y lo resolví con CustomSerializerFactory.

    Este enfoque le permite pasar por alto algún campo específico, ya sea para para todos los objetos, o para tipos específicos.

    public class EntityCustomSerializationFactory extends CustomSerializerFactory {
    
        //ignored fields
        private static final Set<String> IGNORED_FIELDS = new HashSet<String>(
                Arrays.asList(
                        "class",
                        "value",
                        "some"
                )
        );
    
    
        public EntityCustomSerializationFactory() {
            super();
        }
    
        public EntityCustomSerializationFactory(Config config) {
            super(config);
        }
    
        @Override
        protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) {
            super.processViews(config, builder);
    
            //ignore fields only for concrete class
            //note, that you can avoid or change this check
            if (builder.getBeanDescription().getBeanClass().equals(Entity.class)){
                //get original writer
                List<BeanPropertyWriter> originalWriters = builder.getProperties();
    
                //create actual writers
                List<BeanPropertyWriter> writers = new ArrayList<BeanPropertyWriter>();
    
                for (BeanPropertyWriter writer: originalWriters){
                    String propName = writer.getName();
    
                    //if it isn't ignored field, add to actual writers list
                    if (!IGNORED_FIELDS.contains(propName)){
                        writers.add(writer);
                    }
                }
    
                builder.setProperties(writers);
            }
    
        }
    }

    Y después de que usted puede utilizar algo como lo siguiente:

    objectMapper.setSerializerFactory(new EntityCustomSerializationFactory());
    objectMapper.writeValueAsString(new Entity());//response will be without ignored fields
  2. 13

    Suponiendo que el Objetivo de la clase es

    public class Student {
        int age;
        String firstName;
        String lastName;
        double average;
        int numSubjects;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public double getAverage() {
            return average;
        }
    
        public void setAverage(double average) {
            this.average = average;
        }
    
        public int getNumSubjects() {
            return numSubjects;
        }
    
        public void setNumSubjects(int numSubjects) {
            this.numSubjects = numSubjects;
        }
    
    }

    Usted necesita para escribir un serializador personalizado como se indica a continuación

    public class MyCustomSerializer extends JsonSerializer<Student> {
    
        @Override
        public void serialize(Student value, JsonGenerator jgen,
                SerializerProvider provider) throws IOException,
                JsonProcessingException {
            if (value != null) {
                jgen.writeStartObject();
                jgen.writeStringField("age", "Age: " + value.getAge()); //Here a custom way to render age field is used
                jgen.writeStringField("firstName", value.getFirstName());
                jgen.writeStringField("lastName", value.getLastName());
                jgen.writeNumberField("average", value.getAverage());
                jgen.writeNumberField("numSubjects", value.getNumSubjects());
                //Write other properties
                jgen.writeEndObject();
            }
        }
    
    }

    a continuación, añadir a la ObjectMapper

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module = new SimpleModule("custom",
            Version.unknownVersion());
    module.addSerializer(Student.class, new MyCustomSerializer());
    mapper.registerModule(module);

    a continuación, utilizarlo como

    Student s = new Student();
    s.setAge(2);
    s.setAverage(3.4);
    s.setFirstName("first");
    s.setLastName("last");
    s.setNumSubjects(3);
    
    StringWriter sw = new StringWriter();
    mapper.writeValue(sw, s);
    System.out.println(sw.toString());

    Se va a producir una o/p como

    {«edad»:»Edad:
    2″,»nombre»:»primera»,»apellidos»:»último»,»media»:3.4,»numSubjects»:3}

    • Digamos que definir un serializador personalizado para la clase T, que tiene 3 campos y sólo hago una write() en mi serializador (sólo para el campo que quiero anular), serán los otros 2 campos serialzed utilizando valores predeterminados?
    • sí, pero todos los campos de la anulado el tipo de uso que el serializador personalizado
    • Lo consiguió – en mi caso, tengo una clase con campos con tipos nativos.. E. g. int count, largo total, etc. Parece que no se puede utilizar este enfoque en ese caso.
    • Si puedes compartir la clase podemos ver lo que se puede hacer
    • Gracias, he actualizado mi pregunta.
    • permítanos continuar esta discusión en el chat
    • encontrar la respuesta actualizada

  3. 12

    Sólo porque usted no puede modificar las clases NO significa que usted no podría usar anotaciones: sólo tiene que utilizar la mezcla en las anotaciones. Ver este entrada de blog por ejemplo (o google más con «jackson mixin anotaciones») para la utilización de este.

    He usado específicamente Jackson con protobuf – y el ahorro generado por las clases, y funcionan bastante bien. Para antes de Ahorro versiones, he tenido que desactivar la detección de «es-setters», métodos de Ahorro genera a ver si de una propiedad específica ha establecido de forma explícita, pero de lo contrario las cosas funcionaban bien.

    • Que se ve bastante limpio – gracias. Voy a tratar este enfoque también.
    • Sí, es bueno saber que opciones, algunas funcionan mejor en unos, otros en otros casos.
  4. 0

    con la ayuda de @JsonView podemos decidir campos de clases del modelo de serializar que satisfacen los criterios mínimos que ( tenemos que definir los criterios) como podemos tener una clase principal con 10 propiedades, pero sólo 5 de las propiedades se pueden serializar que son necesarias para que el cliente sólo

    Definir nuestros puntos de vista mediante la creación a partir de la clase:

    public class Views
    {
        static class Android{};
        static class IOS{};
        static class Web{};
    }

    Modelo anotado clase con vistas:

    public class Demo 
    {
        public Demo() 
        {
        }
    
    @JsonView(Views.IOS.class)
    private String iosField;
    
    @JsonView(Views.Android.class)
    private String androidField;
    
    @JsonView(Views.Web.class)
    private String webField;
    
     //getters/setters
    ...
    ..
    }

    Ahora tenemos que escribir personalizado json convertidor simplemente ampliando HttpMessageConverter clase a partir de la primavera como:

        public class CustomJacksonConverter implements HttpMessageConverter<Object> 
    {
    public CustomJacksonConverter() 
    {
    super();
    //this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.ClientView.class));
    this.delegate.getObjectMapper().configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
    this.delegate.getObjectMapper().setSerializationInclusion(Include.NON_NULL);
    }
    //a real message converter that will respond to methods and do the actual work
    private MappingJackson2HttpMessageConverter delegate = new MappingJackson2HttpMessageConverter();
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
    return delegate.canRead(clazz, mediaType);
    }
    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
    return delegate.canWrite(clazz, mediaType);
    }
    @Override
    public List<MediaType> getSupportedMediaTypes() {
    return delegate.getSupportedMediaTypes();
    }
    @Override
    public Object read(Class<? extends Object> clazz,
    HttpInputMessage inputMessage) throws IOException,
    HttpMessageNotReadableException {
    return delegate.read(clazz, inputMessage);
    }
    @Override
    public void write(Object obj, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException 
    {
    synchronized(this) 
    {
    String userAgent = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("userAgent");
    if ( userAgent != null ) 
    {
    switch (userAgent) 
    {
    case "IOS" :
    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.IOS.class));
    break;
    case "Android" :
    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView(Views.Android.class));
    break;
    case "Web" :
    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( Views.Web.class));
    break;
    default:
    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
    break;
    }
    }
    else
    {
    //reset to default view
    this.delegate.getObjectMapper().setConfig(this.delegate.getObjectMapper().getSerializationConfig().withView( null ));
    }
    delegate.write(obj, contentType, outputMessage);
    }
    }
    }

    Ahora no hay necesidad de decirle a la primavera para utilizar esta costumbre json convertir simplemente poniendo esto en dispatcher-servlet.xml

    <mvc:annotation-driven>
    <mvc:message-converters register-defaults="true">
    <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter" >
    </bean>
    </mvc:message-converters>
    </mvc:annotation-driven>

    Que es como usted será capaz de decidir qué campos para obtener serializar.

    Gracias

  5. 0

    En caso de que usted no quiere contaminar su modelo con anotaciones, puede utilizar los mixins.

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule simpleModule = new SimpleModule();
    simpleModule.setMixInAnnotation(Student.class, StudentMixin.class);
    mapper.registerModule(simpleModule);

    Y desea reemplazar campo id por ejemplo:

    public abstract class StudentMixin {
    @JsonSerialize(using = StudentIdSerializer.class)
    public String id;
    }

    Hacer lo que sea necesario con el campo:

    public class StudentIdSerializer extends JsonSerializer<Integer> {
    @Override
    public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    jsonGenerator.writeString(String.valueOf(integer * 2));
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here