使用Jackson的@JsonTypeInfo和自定义序列化程序(Using Jackson's @JsonTypeInfo with a custom serializer)
我遇到了Jackson的一个问题,当我使用自定义序列化程序时它不尊重
@JsonTypeInfo
注释。 下面的简化示例不需要自定义序列化,并在不使用自定义序列化程序时按预期输出type
属性。 但是,一旦启用了自定义序列化程序,就不会写入type
属性并阻止反序列化。 这是我的实际系统的测试用例,它确实需要一个自定义序列化程序,如果有所不同,则尝试序列化Map<Enum, Map<Enum, T>>
,其中T
是多态类,其类型信息是没有写。 如何编写自定义序列化程序以正确处理类型信息? 我希望如果我能在下面的测试用例中使用它,我将能够将相同的概念应用于实际代码。测试程序试图尽可能地模拟我在实际应用程序中使用的序列化过程,构建
Map<>
并序列化而不是直接序列化Zoo
。预期产出:
{ "Spike": { "type": "dog", "name": "Spike", "breed": "mutt", "leashColor": "red" }, "Fluffy": { "type": "cat", "name": "Fluffy", "favoriteToy": "spider ring" } }
通过在
SimpleModule
注释掉自定义序列化程序注册,您可以看到输出的类型信息,但是在注册了序列化程序的情况下,输出如上所示,但没有type
属性。import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.Version; import org.codehaus.jackson.annotate.JsonCreator; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonSubTypes; import org.codehaus.jackson.annotate.JsonSubTypes.Type; import org.codehaus.jackson.annotate.JsonTypeInfo; import org.codehaus.jackson.annotate.JsonTypeInfo.As; import org.codehaus.jackson.annotate.JsonTypeInfo.Id; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.Module; import org.codehaus.jackson.map.Module.SetupContext; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ResolvableSerializer; import org.codehaus.jackson.map.SerializationConfig.Feature; import org.codehaus.jackson.map.SerializerProvider; import org.codehaus.jackson.map.module.SimpleModule; import org.codehaus.jackson.map.module.SimpleSerializers; public class JacksonTest { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Module m = new SimpleModule("TestModule", new Version(1,0,0,"")) { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.setMixInAnnotations(Animal.class, AnimalMixIn.class); SimpleSerializers serializers = new SimpleSerializers(); serializers.addSerializer(Zoo.class, new ZooSerializer()); context.addSerializers(serializers); } }; mapper.registerModule(m); mapper.configure(Feature.INDENT_OUTPUT, true); Zoo zoo = new Zoo(); List<Animal> animals = new ArrayList<Animal>(); animals.add(new Dog("Spike", "mutt", "red")); animals.add(new Cat("Fluffy", "spider ring")); zoo.animals = animals; System.out.println(zoo); String json = mapper.writeValueAsString(zoo); System.out.println(json); } static class Zoo { public Collection<Animal> animals = Collections.EMPTY_SET; public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Zoo: { "); for (Animal animal : animals) sb.append(animal.toString()).append(" , "); return sb.toString(); } } static class ZooSerializer extends JsonSerializer<Zoo> { @Override public void serialize(Zoo t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException { Map<Object, Animal> animalMap = new HashMap<Object, Animal>(); for (Animal a : t.animals) animalMap.put(a.getName(), a); jg.writeObject(animalMap); } } @JsonTypeInfo( use=Id.NAME, include=As.PROPERTY, property="type") @JsonSubTypes({ @Type(value=Cat.class,name="cat"), @Type(value=Dog.class,name="dog") }) static abstract class AnimalMixIn { } static interface Animal<T> { T getName(); } static abstract class AbstractAnimal<T> implements Animal<T> { private final T name; protected AbstractAnimal(T name) { this.name = name; } public T getName() { return name; } } static class Dog extends AbstractAnimal<String> { private final String breed; private final String leashColor; @JsonCreator public Dog(@JsonProperty("name") String name, @JsonProperty("breed") String breed, @JsonProperty("leashColor") String leashColor) { super(name); this.breed = breed; this.leashColor = leashColor; } public String getBreed() { return breed; } public String getLeashColor() { return leashColor; } @Override public String toString() { return "Dog{" + "name=" + getName() + ", breed=" + breed + ", leashColor=" + leashColor + "}"; } } static class Cat extends AbstractAnimal<String> { private final String favoriteToy; @JsonCreator public Cat(@JsonProperty("name") String name, @JsonProperty("favoriteToy") String favoriteToy) { super(name); this.favoriteToy = favoriteToy; } public String getFavoriteToy() { return favoriteToy; } @Override public String toString() { return "Cat{" + "name=" + getName() + ", favoriteToy=" + favoriteToy + '}'; } } }
编辑:添加可能澄清问题的其他测试用例
在阅读了关于类似问题的更多问题之后,我决定尝试修改我的自定义序列化程序以缩小问题所在。 我发现将
Animal
对象添加到任何通用类型的Collection(List<Animal>
和Map<Object, Animal>
都进行了测试),类型信息未被序列化。 但是,在序列化Animal[]
,包含了类型信息。 不幸的是,虽然我可以在测试代码中改变这种行为,但我需要生成代码来序列化具有多态值的Map
。将自定义
ZooSerializer.serialize()
方法更改为以下输出类型信息,但丢失了我需要的Map
语义:public void serialize(...) { Animal[] animals = t.animals.toArray(new Animal[0]); jg.writeObject(animals); }
I'm running into a problem with Jackson where it does not respect the
@JsonTypeInfo
annotations when I use a custom serializer. The simplified example below does not require the custom serialization and outputs thetype
property as expected when I don't use the custom serializer. However, once the custom serializer has been enabled, thetype
property is not written and prevents deserialization. This is a test case for my actual system, which does require a custom serializer and, if it makes a difference, is attempting to serialize aMap<Enum, Map<Enum, T>>
whereT
is the polymorphic class whose type information is not being written. How can I write the custom serializer so it correctly handles the type information? I'm hoping that if I can get it working for the test case below, I will be able to apply the same concepts to the actual code.The test program attempts to simulate as closely as possible the serialization process I'm using in the real application, building a
Map<>
and serializing that instead of serializing theZoo
directly.Expected Output:
{ "Spike": { "type": "dog", "name": "Spike", "breed": "mutt", "leashColor": "red" }, "Fluffy": { "type": "cat", "name": "Fluffy", "favoriteToy": "spider ring" } }
By commenting out the custom serializer registration in the
SimpleModule
you can see the type information is output, but with the serializer registered, the output is as above but without thetype
property.import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codehaus.jackson.JsonGenerator; import org.codehaus.jackson.JsonProcessingException; import org.codehaus.jackson.Version; import org.codehaus.jackson.annotate.JsonCreator; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.annotate.JsonSubTypes; import org.codehaus.jackson.annotate.JsonSubTypes.Type; import org.codehaus.jackson.annotate.JsonTypeInfo; import org.codehaus.jackson.annotate.JsonTypeInfo.As; import org.codehaus.jackson.annotate.JsonTypeInfo.Id; import org.codehaus.jackson.map.JsonMappingException; import org.codehaus.jackson.map.JsonSerializer; import org.codehaus.jackson.map.Module; import org.codehaus.jackson.map.Module.SetupContext; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ResolvableSerializer; import org.codehaus.jackson.map.SerializationConfig.Feature; import org.codehaus.jackson.map.SerializerProvider; import org.codehaus.jackson.map.module.SimpleModule; import org.codehaus.jackson.map.module.SimpleSerializers; public class JacksonTest { public static void main(String[] args) throws Exception { ObjectMapper mapper = new ObjectMapper(); Module m = new SimpleModule("TestModule", new Version(1,0,0,"")) { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.setMixInAnnotations(Animal.class, AnimalMixIn.class); SimpleSerializers serializers = new SimpleSerializers(); serializers.addSerializer(Zoo.class, new ZooSerializer()); context.addSerializers(serializers); } }; mapper.registerModule(m); mapper.configure(Feature.INDENT_OUTPUT, true); Zoo zoo = new Zoo(); List<Animal> animals = new ArrayList<Animal>(); animals.add(new Dog("Spike", "mutt", "red")); animals.add(new Cat("Fluffy", "spider ring")); zoo.animals = animals; System.out.println(zoo); String json = mapper.writeValueAsString(zoo); System.out.println(json); } static class Zoo { public Collection<Animal> animals = Collections.EMPTY_SET; public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Zoo: { "); for (Animal animal : animals) sb.append(animal.toString()).append(" , "); return sb.toString(); } } static class ZooSerializer extends JsonSerializer<Zoo> { @Override public void serialize(Zoo t, JsonGenerator jg, SerializerProvider sp) throws IOException, JsonProcessingException { Map<Object, Animal> animalMap = new HashMap<Object, Animal>(); for (Animal a : t.animals) animalMap.put(a.getName(), a); jg.writeObject(animalMap); } } @JsonTypeInfo( use=Id.NAME, include=As.PROPERTY, property="type") @JsonSubTypes({ @Type(value=Cat.class,name="cat"), @Type(value=Dog.class,name="dog") }) static abstract class AnimalMixIn { } static interface Animal<T> { T getName(); } static abstract class AbstractAnimal<T> implements Animal<T> { private final T name; protected AbstractAnimal(T name) { this.name = name; } public T getName() { return name; } } static class Dog extends AbstractAnimal<String> { private final String breed; private final String leashColor; @JsonCreator public Dog(@JsonProperty("name") String name, @JsonProperty("breed") String breed, @JsonProperty("leashColor") String leashColor) { super(name); this.breed = breed; this.leashColor = leashColor; } public String getBreed() { return breed; } public String getLeashColor() { return leashColor; } @Override public String toString() { return "Dog{" + "name=" + getName() + ", breed=" + breed + ", leashColor=" + leashColor + "}"; } } static class Cat extends AbstractAnimal<String> { private final String favoriteToy; @JsonCreator public Cat(@JsonProperty("name") String name, @JsonProperty("favoriteToy") String favoriteToy) { super(name); this.favoriteToy = favoriteToy; } public String getFavoriteToy() { return favoriteToy; } @Override public String toString() { return "Cat{" + "name=" + getName() + ", favoriteToy=" + favoriteToy + '}'; } } }
EDIT: Adding additional test cases that may clarify the problem
After reading some more questions about similar issues, I decided to try modifying my custom serializer to narrow down where the problem is. I discovered that adding my
Animal
objects to any generically typed Collection (List<Animal>
andMap<Object, Animal>
were tested), the type information was not serialized. However, when serializing anAnimal[]
, the type information was included. Unfortunately, while I can vary that behavior in the test code, I need my production code to serialize aMap
with polymorphic values.Changing the custom
ZooSerializer.serialize()
method to the following does output type information, but loses theMap
semantics I need:public void serialize(...) { Animal[] animals = t.animals.toArray(new Animal[0]); jg.writeObject(animals); }
原文:https://stackoverflow.com/questions/8140697
最满意答案
创建一个Calendar对象是一个相对缓慢而复杂的动作,它必须弥补日期和时间固有的所有奇怪现象,如时区,夏令时等。
您最好的选择是使用Date对象作为第一个示例,请参阅此处以获取有关Date,Calendar和SimpleDateFormat的性能指标的比较。
另一种方法可能是使用JodaTime ,请参阅此处以了解JodaTime和Calendar之间的性能比较。 虽然Java Date和JodaTime之间的差异在性能方面可能微不足道,但JodaTime通常被认为是处理日期和时间的更好方法。 出于您的目的,导入JodaTime仅用于时间戳可能会有点过分。
Creating a Calendar object is a relatively slow and complex action, it has to compensate for all the oddities that are inherent to dates and times such as timezones, daylight savings etc.
Your best bet is to use the Date object as your first example, see here for a comparison on performance metrics of Date, Calendar and SimpleDateFormat.
Another approach could be to use JodaTime, see here for performance comparison between JodaTime and Calendar. Whilst the difference between Java Date and JodaTime is probably negligible in performance terms, JodaTime is generally agreed to be a better way of handling dates and times. For your purpose, importing JodaTime just for a timestamp may well be overkill though.
相关问答
更多-
仅供参考,一个月由0到11之间的整数表示; 0是1月,1是2月,依此类推; 因此11月是12月。 您可以在此处查看有关日期的更多信息以及有关SimpleDateFormat的更多信息。 更新: 关于getMonth() =>的另一件事是不推荐使用,而不是Calendar.get(Calendar.MONTH) ,请点击此处 。 FYI, A month is represented by an integer from 0 to 11; 0 is January, 1 is February, and so ...
-
从Date对象中删除时间?(Removing time from a Date object?)[2023-08-08]
快速回答是: 不,你不允许这样做。 因为这是Date使用的。 来自Date javadoc: Class Date代表一个特定的时间,以毫秒的精度。 但是 ,由于这个类只是一个数据对象。 它不关心我们如何描述它。 当我们看到2012/01/01 12:05:10.321的日期时,可以说是2012/01/01 ,这就是你所需要的。 有很多方法可以做到这一点。 示例1:通过操纵字符串 输入字符串:2012/01/20 12:05:10.321 所需输出字符串:2012/01/20 由于yyyy / MM / d ... -
由于getXXXFormatter()方法仅使用today字段,而构造函数Date2(int, int, int)从不更改该字段,因此它们将始终格式化今天的日期。 您可能希望像这样更改该构造函数: public Date2(int dd, int mm, int yyyy) { this.day = dd; this.month = mm; this.year = yyyy; Calendar calendar = Calendar.getInstance(); c ...
-
您还可以使用lubridate包( lubridate一部分),它具有使用ymd_hm()等函数将字符串转换为日期或日期时间对象的功能。 library(tidyverse) library(lubridate) df <- tribble(~Date, ~Time, ~Open, ~High, ~Low, "2017.09.01", "00:00", "1.19013", "1.19017", "1.19013", "2017.09.01", "00:01", "1.19015", "1. ...
-
settings=Settings.objects.get(user=2) if user.date_format == '0': date=datetime.datetime.strptime('%m/%d/%Y').strftime('%d/%m/%Y') else: date=datetime.datetime.strptime('%d/%m/%Y').strftime('%m/%d/%Y') 您可能希望使用settings.date_form ...
-
如何使用Calendar将日期字符串转换为不同的格式(How to convert date strings to different format using Calendar)[2022-04-12]
更新过的: // Set the time in String String stringDate = "04:10:13"; // Parse this time SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); Calendar cal = Calendar.getInstance(); // Set the parsed time to a Calendar object cal.setTime(sdf.parse(stringDate ... -
创建一个Calendar对象是一个相对缓慢而复杂的动作,它必须弥补日期和时间固有的所有奇怪现象,如时区,夏令时等。 您最好的选择是使用Date对象作为第一个示例,请参阅此处以获取有关Date,Calendar和SimpleDateFormat的性能指标的比较。 另一种方法可能是使用JodaTime ,请参阅此处以了解JodaTime和Calendar之间的性能比较。 虽然Java Date和JodaTime之间的差异在性能方面可能微不足道,但JodaTime通常被认为是处理日期和时间的更好方法。 出于您的目 ...
-
Date的字符串格式(string format of Date)[2022-03-31]
这里有三个问题: 正则表达式不会捕获整个日期和时间 你只是得到正则表达式结果的一部分(即你得到一个单一的组,而不是整个比赛的值) 你的格式字符串使用zzz时,它应该是千分之一的FF或数千的FFF 尝试这样的事情: string lineOfLog = "- Started 28/12/2014 16:53:47.48"; string dateFormat = "dd/MM/yyyy HH:mm:ss.FF"; string pattern1 = @"(\d+)/(\d+)/(\d+) ... -
String mytime="20120215102400"; SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyyMMddhhmmss"); try { Date myDate = dateFormat.parse(mytime); } catch (ParseException e) { e.printStackT ...
-
您可以使用SimpleDateFormat来解析日期和时间。 就像是 DateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm"); Calendar cal = Calendar.getInstance(); try { cal.setTime(sdf.parse(String.format("%s %s", date, time))); // use cal.... } catch (ParseException e) { e ...