Jackson - custom serializer that overrides only specific fields
我知道如何在Jackson中使用自定义序列化程序(通过扩展
注释不是一个选项,因为我正在序列化一个生成的类(节俭)。
在编写自定义Jackson序列化程序时,如何仅指定要重写的某些字段?
更新:
下面是我要序列化的类:
1 2 3 4 5 6 7 8 9 |
上面的类有许多属性,其中大多数使用本地类型。我只想重写自定义序列化程序中的一些属性,让Jackson像往常一样处理其余的属性。例如,我只想将"年龄"字段转换为自定义输出。
不能修改类并不意味着不能使用注释:只需在注释中使用mix。如需了解如何使用此功能,请参阅此博客条目(或Google了解更多关于"Jackson Mixin Annotations"的内容)。
我在Protobuf和Thrift生成的类中专门使用了Jackson,它们工作得很好。对于早期的节俭版本,我不得不禁用对"is setters"的发现,节俭生成的方法用于查看是否已显式设置了特定属性,但其他方面的工作正常。
假设你的目标类是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 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; } } |
您需要编写一个自定义序列化程序,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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(); } } } |
号
然后将其添加到对象映射器
1 2 3 4 5 | ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule("custom", Version.unknownVersion()); module.addSerializer(Student.class, new MyCustomSerializer()); mapper.registerModule(module); |
然后像这样使用
1 2 3 4 5 6 7 8 9 10 | 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()); |
。
它会产生一个O/P
{"age":"Age:
2","firstName":"first","lastName":"last","average":3.4,"numSubjects":3}
号
我也面临同样的问题,我用CustomSerializerFactory解决了它。
这种方法允许您忽略所有对象或特定类型的某些特定字段。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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); } } } |
之后,您可以使用它,如下所示:
1 2 | objectMapper.setSerializerFactory(new EntityCustomSerializationFactory()); objectMapper.writeValueAsString(new Entity());//response will be without ignored fields |
。
如果不想用注释污染模型,可以使用mixin。
1 2 3 4 | ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); simpleModule.setMixInAnnotation(Student.class, StudentMixin.class); mapper.registerModule(simpleModule); |
。
并且要覆盖ID字段,例如:
1 2 3 4 | public abstract class StudentMixin { @JsonSerialize(using = StudentIdSerializer.class) public String id; } |
号
用这个领域做你需要做的一切:
1 2 3 4 5 6 | public class StudentIdSerializer extends JsonSerializer<Integer> { @Override public void serialize(Integer integer, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(String.valueOf(integer * 2)); } } |
号
在@jsonview的帮助下,我们可以决定要序列化的模型类的字段满足最小条件(我们必须定义条件),就像我们可以有一个具有10个属性的核心类,但只有5个属性可以序列化,这仅对客户端是必需的
通过创建以下类定义视图:
1 2 3 4 5 6 | public class Views { static class Android{}; static class IOS{}; static class Web{}; } |
。
带视图的注释模型类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
现在,我们必须通过简单地从Spring扩展httpMessageConverter类来编写自定义JSON转换器,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 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); } } } |
。
现在需要告诉Spring使用这个定制的JSON转换,只需将其放入dispatcher-servlet.xml中即可。
1 2 3 4 5 6 | <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean id="jsonConverter" class="com.mactores.org.CustomJacksonConverter"> </bean> </mvc:message-converters> </mvc:annotation-driven> |
这就是如何决定要序列化哪些字段的方法。
桑克斯