我正在使用Jackson来反序列化Product
接口的许多不同实现。这些产品实现具有不同的字段,但都具有InsuredAmount
字段。此InsuredAmount
类具有值字段和IAType
字段。 IAType
是一个标记接口,具有不同的枚举作为实现。
现在问题在于:IAType
接口的枚举实现对应于Product
接口的某个实现。如何制作通用实现并告诉Jackson找到正确的IAType实现?我应该在Product和IAType接口上使用通用参数来识别产品实现吗?我应该在识别产品实现的类上使用Productable
功能接口吗?我如何告诉杰克逊使用该实现?
我希望下面的代码能够澄清问题,我选择在这里实现一个Productable
接口,但是欢迎使用更好的结构来处理这个问题。
@JsonPropertyOrder({"type", "someInfo"})
public class InsuredAmount implements Productable, Serializable {
private static final long serialVersionUID = 1L;
private IAType type;
private String someInfo;
public InsuredAmount() {
}
public InsuredAmount(IAType typeA, String someInfo) {
this.type = typeA;
this.someInfo = someInfo;
}
/* This should be on the product level, but if I can solve this problem,
the next level will just be more of the same.
*/
@JsonIgnore
@Override
public Product getProduct() {
return Product.PROD_A;
}
// Getters, setters, equals, etc. omitted.
}
-
public interface Productable {
public Product getProduct();
}
-
public enum Product {
PROD_A, PROD_B;
}
-
@JsonDeserialize(using = IATypeDeserializer.class)
public interface IAType extends Productable {
}
-
public enum IATypeA implements IAType {
FOO, BAR;
@Override
public Product getProduct() {
return Product.PROD_A;
}
}
-
public class IATypeDeserializer extends StdDeserializer<IAType> {
private static final long serialVersionUID = 1L;
public IATypeDeserializer() {
this(null);
}
public IATypeDeserializer(Class<?> vc) {
super(vc);
}
@Override
public IAType deserialize(JsonParser parser, DeserializationContext context)
throws IOException, JsonProcessingException {
JsonNode node = parser.getCodec().readTree(parser);
/* How to find out that the class calling the deserialization is InsuredAmountA, which
has getProduct() method that returns PROD_A, and matches the IATypeA that also returns
PROD_A, so I know to deserialize IATypeA, instead of other implementations of the IAType
interface?
*/
return IATypeA.valueOf(node.asText());
}
}
-
public class InsuredAmountTest {
private final ObjectMapper mapper = new ObjectMapper();
@Test
public void test01() throws IOException {
InsuredAmount iaA = new InsuredAmount(IATypeA.FOO, "test it");
String json = mapper.writeValueAsString(iaA);
assertThat(json, is("{\"type\":\"FOO\",\"someInfo\":\"test it\"}"));
InsuredAmount iaA2 = mapper.readValue(json, InsuredAmount.class);
IAType type = iaA2.getType();
assertThat(type, is(IATypeA.FOO));
assertThat(type.getProduct(), is(Product.PROD_A));
assertThat(iaA, is(iaA2));
}
@Test
public void test02() throws IOException {
InsuredAmount iaA = new InsuredAmount(IATypeA.BAR, "test it");
String json = mapper.writeValueAsString(iaA);
assertThat(json, is("{\"type\":\"BAR\",\"someInfo\":\"test it\"}"));
InsuredAmount iaA2 = mapper.readValue(json, InsuredAmount.class);
assertThat(iaA, is(iaA2));
}
}
答案 0 :(得分:3)
杰克逊处理枚举序列化的过程非常简单,因此您需要做的只是使用IAType
注释@JsonTypeInfo
字段:
@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
private IAType type;
然后进行测试:
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(new InsuredAmount(IATypeA.FOO, "info"));
System.out.println(json);
InsuredAmount ia = mapper.readValue(json, InsuredAmount.class);
System.out.println("Type is: " + ia.getType());
}
导致输出:
{"type":[".IATypeA","FOO"],"someInfo":"info"}
Type is: FOO
要获得更紧凑的表示,您必须使用自定义序列化。假设您的枚举命名空间中没有重叠,您可以将类型字段序列化为枚举名称。
反序列化器需要知道哪些类型可用于构造,可以通过类路径发现,或者如下例所示,简单地对引用进行硬编码:
public class IATest {
public static class IATypeSerializer extends JsonSerializer<IAType> {
@Override
public void serialize(IAType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(((Enum) value).name());
}
}
public static class IATypeDeserializer extends JsonDeserializer<IAType> {
@Override
public IAType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.readValueAs(String.class);
try {
return IATypeA.valueOf(value);
} catch (IllegalArgumentException e) {
// fall through
}
try {
return IATypeB.valueOf(value);
} catch (IllegalArgumentException e) {
// fall through
}
throw new JsonMappingException(p, "Unknown type '" + value + "'");
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
// Register a module to handle serialization of IAType implementations
SimpleModule module = new SimpleModule();
module.addSerializer(IAType.class, new IATypeSerializer());
module.addDeserializer(IAType.class, new IATypeDeserializer());
mapper.registerModule(module);
// Test
String json = mapper.writeValueAsString(new InsuredAmount(IATypeA.FOO, "info"));
System.out.println(json);
InsuredAmount ia = mapper.readValue(json, InsuredAmount.class);
System.out.println("Type is: " + ia.getType());
}
}
哪个输出:
{"type":"FOO","someInfo":"info"}
Type is: FOO
答案 1 :(得分:2)
我最终在特殊构造函数上使用JsonCreator
注释。
@JsonCreator
public InsuredAmountA(
@JsonProperty("type") String type,
@JsonProperty("someInfo") String someInfo) throws IOException {
switch (getProduct()) {
case PROD_A:
try {
this.type = IATypeA.valueOf(type);
break;
} catch (IllegalArgumentException ex) {
// Throw IOException in the default.
}
// case PROD_B:
// this.type = (IATypeB) typeA;
// break;
default:
throw new IOException(String.format("Cannot parse value %s as type.", type));
}
this.someInfo = someInfo;
}
答案 2 :(得分:0)