泽西岛解析JSON / Jackson子类型反序列化的规则

我收到JSON的方式:

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

JSON的示例:

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

我有一个类结构(它们是由jsonschema2pojo生成的,假设没关系):

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

如何让Jersey解析JSON,以便在eventType = 1时,stats.params成为Params1和Params2的实例?

今天早上我花了一些时间来解决这个问题。 一个有趣的用例。 我想出了怎么做,但我不得不稍微改变你的json。 这不是绝对必要的,但是类型转换不是您的问题的一部分,因此我们可以根据需要进行跟进:)

你的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" } } 

我正在使用这两个文件来执行请求。 请注意,我将eventType更改为String而不是数字。 我稍后会指出这一点。

你的模型对象:

 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; } } 

我正在使用JsonTypeInfo,这是这样做的:

 JsonTypeInfo.Id.NAME 

逻辑类型名称,在您的情况下,它是属性“eventType”

 JsonTypeInfo.As.EXTERNAL_PROPERTY 

表示外部属性用于反序列化上下文。 您只能在属性上使用它 ,而不能在Params本身上使用类注释。 这就是为什么我注释setter方法而不是接口类。

 property="eventType" 

只需告诉jackson要使用的属性名称

然后在JsonSubTypes中我注释了可能的选项,在你的情况2中:

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

这告诉jackson在事件类型属性为“1”的情况下使用Param1.class

因此,对于PAram2.class和属性值为“2”相同

注意这就是为什么我稍微改变了json。 子类型注释不能将整数作为属性。 现在您可以使用不同的选项,例如TypeConverters在运行时将您的整数属性转换为字符串,这样您就可以保持json相同。 我跳过了这一步,快速谷歌会给你如何做到这一点的说明。

现在你的参数模型看起来像这样:

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

我正在对这些属性进行注释,因此jackson知道要对这些属性进行反序列化。

注意我有一个问题,因为有两个属性让我懒惰的眼睛看起来一样:

 JsonTypeInfo.As.EXTERNAL_PROPERTY JsonTypeInfo.As.EXISTING_PROPERTY 

你不能使用现有的:D这需要花费十分钟才能搞清楚。 有趣的事实,我上面有两行,并且一直在评论一个,没有理解为什么他们中的一个在另一个工作时抛出exception。

无论如何。

最后测试:

 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 

请注意,资源正在打印实例化类的名称。 正如您所看到的,两个json已被反序列化为正确的实例类。

我希望有帮助:)

阿图尔

(有趣的事实#2:在我的回答中,我也使用了EXISTING而不是EXTERNAL,只是没有看到它。我可能需要让jackson为了我的理智而改变他们的名字)

编辑

我刚尝试过,jackson很聪明,可以为你转换你的json。 因此,您可以按原样保留json,并将模型中的属性作为String(如图所示)。 一切正常。

但是为了完整性,如果你想要一个转换器(因为你可能需要将你的字符串模型转换回一个整数来进行serailisation),这将是一个整数到字符串的转换器:

 public class EventTypeConverter implements Converter{ @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); } } 

你可以这样做:

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