How do I use a custom Serializer with Jackson?
我有两个Java类,我想使用杰克逊将其序列化为JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class User { public final int id; public final String name; public User(int id, String name) { this.id = id; this.name = name; } } public class Item { public final int id; public final String itemNr; public final User createdBy; public Item(int id, String itemNr, User createdBy) { this.id = id; this.itemNr = itemNr; this.createdBy = createdBy; } } |
我想将一个项目序列化到这个JSON:
1 | {"id":7,"itemNr":"TEST","createdBy":3} |
用户序列化为只包含
1 |
所以我想我需要为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class ItemSerializer extends JsonSerializer<Item> { @Override public void serialize(Item value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeStartObject(); jgen.writeNumberField("id", value.id); jgen.writeNumberField("itemNr", value.itemNr); jgen.writeNumberField("createdBy", value.user.id); jgen.writeEndObject(); } } |
我用Jackson How to:自定义序列化程序中的代码序列化JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule("SimpleModule", new Version(1,0,0,null)); simpleModule.addSerializer(new ItemSerializer()); mapper.registerModule(simpleModule); StringWriter writer = new StringWriter(); try { mapper.writeValue(writer, myItem); } catch (JsonGenerationException e) { e.printStackTrace(); } catch (JsonMappingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } |
但我得到这个错误:
1 2 3 4 | Exception in thread"main" java.lang.IllegalArgumentException: JsonSerializer of type com.example.ItemSerializer does not define valid handledType() (use alternative registration method?) at org.codehaus.jackson.map.module.SimpleSerializers.addSerializer(SimpleSerializers.java:62) at org.codehaus.jackson.map.module.SimpleModule.addSerializer(SimpleModule.java:54) at com.example.JsonTest.main(JsonTest.java:54) |
如何在Jackson中使用自定义序列化程序?
这就是我如何处理GSON:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class UserAdapter implements JsonSerializer<User> { @Override public JsonElement serialize(User src, java.lang.reflect.Type typeOfSrc, JsonSerializationContext context) { return new JsonPrimitive(src.id); } } GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(User.class, new UserAdapter()); Gson gson = builder.create(); String json = gson.toJson(myItem); System.out.println("JSON:"+json); |
但我现在需要和杰克逊一起做,因为GSON不支持接口。
可以将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class CustomDateSerializer extends SerializerBase<Date> { public CustomDateSerializer() { super(Date.class, true); } @Override public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { SimpleDateFormat formatter = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZZ (z)"); String format = formatter.format(value); jgen.writeString(format); } } |
如前所述,@jsonvalue是一种好方法。但是,如果您不介意使用自定义序列化程序,就不需要为项编写一个序列化程序,而需要为用户编写一个序列化程序——如果是这样,它将简单如下:
1 2 3 4 5 | public void serialize(Item value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { jgen.writeNumber(id); } |
另一种可能是实施
至于错误,这很奇怪——您可能想升级到更高版本。但是扩展
我也尝试过这样做,但Jackson网页上的示例代码中有一个错误,即在对addSerializer方法的调用中没有包含类型(.class),其内容如下:
1 | simpleModule.addSerializer(Item.class, new ItemSerializer()); |
换句话说,这些是实例化simplemodule并添加序列化程序的行(前面的错误行被注释掉):
1 2 3 4 5 6 | ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule("SimpleModule", new Version(1,0,0,null)); // simpleModule.addSerializer(new ItemSerializer()); simpleModule.addSerializer(Item.class, new ItemSerializer()); mapper.registerModule(simpleModule); |
仅供参考:以下是正确示例代码的参考:http://wiki.fasterxml.com/jacksonfeaturemodules
希望这有帮助!
使用@ JsonValue:
1 2 3 4 5 6 7 8 9 |
@jsonValue只对方法有效,因此必须添加getid方法。您应该能够完全跳过自定义序列化程序。
这些是我在理解Jackson序列化时注意到的行为模式。
1)假设有一个目标教室和一个班学生。我已经把所有的事情都公之于众了。
1 2 3 4 5 6 7 8 9 10 |
2)假设这些是我们用于将对象序列化为JSON的序列化程序。如果对象已在对象映射器中注册,则WriteObjectField使用该对象自己的序列化程序;否则,则将其序列化为POJO。WriteNumberField只接受基元作为参数。
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 | public class ClassroomSerializer extends StdSerializer<Classroom> { public ClassroomSerializer(Class<Classroom> t) { super(t); } @Override public void serialize(Classroom value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeStartObject(); jgen.writeObjectField("double1-Object", value.double1); jgen.writeNumberField("double1-Number", value.double1); jgen.writeObjectField("Double1-Object", value.Double1); jgen.writeNumberField("Double1-Number", value.Double1); jgen.writeObjectField("student1", value.student1); jgen.writeEndObject(); } } public class StudentSerializer extends StdSerializer<Student> { public StudentSerializer(Class<Student> t) { super(t); } @Override public void serialize(Student value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { jgen.writeStartObject(); jgen.writeObjectField("double2-Object", value.double2); jgen.writeNumberField("double2-Number", value.double2); jgen.writeObjectField("Double2-Object", value.Double2); jgen.writeNumberField("Double2-Number", value.Double2); jgen.writeEndObject(); } } |
3)在simplemodule中只注册输出模式为
1 2 3 4 5 6 7 8 9 10 11 12 | { "double1" : 1234.5678, "Double1" : { "value" :"91,011.121" }, "student1" : { "double2" : 1920.2122, "Double2" : { "value" :"2,324.253" } } } |
您可以看到,POJO序列化区分了double和double,对double使用doubleSerialzer,对double使用常规字符串格式。
4)注册DoubleSerializer和ClassRoomSerializer,不使用studentSerializer。我们期望输出是这样的:如果我们将double作为一个对象写入,它的行为就像一个double;如果我们将double作为一个数字写入,它的行为就像一个double。student实例变量应该作为pojo编写,并遵循上面的模式,因为它不注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | { "double1-Object" : { "value" :"1,234.568" }, "double1-Number" : 1234.5678, "Double1-Object" : { "value" :"91,011.121" }, "Double1-Number" : 91011.1213, "student1" : { "double2" : 1920.2122, "Double2" : { "value" :"2,324.253" } } } |
5)注册所有序列化程序。输出是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | { "double1-Object" : { "value" :"1,234.568" }, "double1-Number" : 1234.5678, "Double1-Object" : { "value" :"91,011.121" }, "Double1-Number" : 91011.1213, "student1" : { "double2-Object" : { "value" :"1,920.212" }, "double2-Number" : 1920.2122, "Double2-Object" : { "value" :"2,324.253" }, "Double2-Number" : 2324.2526 } } |
与预期完全一致。
另一个重要注意事项:如果在同一模块中注册了同一类的多个序列化程序,则该模块将为最近添加到列表中的该类选择序列化程序。这不应该使用-这很混乱,我不确定这有多一致
道德:如果要自定义对象中原语的序列化,必须为该对象编写自己的序列化程序。您不能依赖Pojo Jackson序列化。
在我的例子中(Spring3.2.4和Jackson2.3.1),自定义序列化程序的XML配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <mvc:annotation-driven> <mvc:message-converters register-defaults="false"> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"> <property name="serializers"> <bean class="com.example.business.serializer.json.CustomObjectSerializer"/> </array> </property> </bean> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> |
以无法解释的方式被某些东西覆盖回到默认状态。
这对我很有用:
自定义对象.java1 2 3 4 5 6 7 8 9 10 11 12 13 |
自定义对象序列化程序.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class CustomObjectSerializer extends JsonSerializer<CustomObject> { @Override public void serialize(CustomObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException,JsonProcessingException { jgen.writeStartObject(); jgen.writeNumberField("y", value.getValue()); jgen.writeEndObject(); } @Override public Class<CustomObject> handledType() { return CustomObject.class; } } |
我的解决方案不需要XML配置(
Jackson的JSON视图可能是实现需求的一种简单方法,特别是在JSON格式中具有一定的灵活性的情况下。
如果
您只需将用户的名称字段注释为视图的一部分,并在序列化请求中指定其他视图(默认情况下,未注释的字段将包括在内)。
例如:定义视图:
1 2 3 4 | public class Views { public static class BasicView{} public static class CompleteUserView{} } |
注释用户:
1 2 3 4 5 6 7 8 9 10 11 |
并序列化请求不包含要隐藏的字段的视图(默认情况下,非注释字段是序列化的):
1 | objectMapper.getSerializationConfig().withView(Views.BasicView.class); |
我为定制的
创建对象映射器时,请执行以下操作:
1 2 3 4 5 6 7 8 9 10 11 12 | public class JsonUtils { public static ObjectMapper objectMapper = null; static { objectMapper = new ObjectMapper(); SimpleModule s = new SimpleModule(); s.addSerializer(Timestamp.class, new TimestampSerializerTypeHandler()); s.addDeserializer(Timestamp.class, new TimestampDeserializerTypeHandler()); objectMapper.registerModule(s); }; } |
例如,在
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 | import java.time.LocalDateTime; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; @Provider public class JacksonConfig implements ContextResolver<ObjectMapper> { private final ObjectMapper objectMapper; public JacksonConfig() { objectMapper = new ObjectMapper(); SimpleModule s = new SimpleModule(); s.addSerializer(Timestamp.class, new TimestampSerializerTypeHandler()); s.addDeserializer(Timestamp.class, new TimestampDeserializerTypeHandler()); objectMapper.registerModule(s); }; @Override public ObjectMapper getContext(Class<?> type) { return objectMapper; } } |
序列化程序应该是这样的:
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 | import java.io.IOException; import java.sql.Timestamp; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; public class TimestampSerializerTypeHandler extends JsonSerializer<Timestamp> { @Override public void serialize(Timestamp value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { String stringValue = value.toString(); if(stringValue != null && !stringValue.isEmpty() && !stringValue.equals("null")) { jgen.writeString(stringValue); } else { jgen.writeNull(); } } @Override public Class<Timestamp> handledType() { return Timestamp.class; } } |
反序列化程序如下:
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 | import java.io.IOException; import java.sql.Timestamp; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.SerializerProvider; public class TimestampDeserializerTypeHandler extends JsonDeserializer<Timestamp> { @Override public Timestamp deserialize(JsonParser jp, DeserializationContext ds) throws IOException, JsonProcessingException { SqlTimestampConverter s = new SqlTimestampConverter(); String value = jp.getValueAsString(); if(value != null && !value.isEmpty() && !value.equals("null")) return (Timestamp) s.convert(Timestamp.class, value); return null; } @Override public Class<Timestamp> handledType() { return Timestamp.class; } } |
你必须重写方法handledtype,一切都能正常工作。
1 2 3 4 5 | @Override public Class<Item> handledType() { return Item.class; } |
如果您在自定义序列化程序中的唯一要求是跳过对
还参见:为什么Java有瞬态字段?]