2017-02-03 3 views
5

Я использую Jackson для десериализации ряда различных реализаций интерфейса Product. Эти реализации продукта имеют разные поля, но у всех есть поле InsuredAmount. Класс InsuredAmount имеет поле значений и поле IAType. IAType является интерфейсом маркера с различными перечислениями в качестве реализаций.Как десериализовать на основе информации, доступной в родительском классе

Теперь вот в чем проблема: реализация enum интерфейса IAType соответствует определенной реализации интерфейса Product. Как я могу сделать общую реализацию и сказать Джексону, чтобы найти правильную реализацию вашего IAType? Должен ли я использовать общий параметр для продукта и интерфейса 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)); 
    } 

} 

ответ

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

Вы можете смотреть в направлении полиморфного десериализация:

http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

unsing пользовательского типа распознавателя

+0

Да, я уже изучил это. Не удалось выяснить, как это использовать в моем случае. –

+0

@JsonTypeResolver, похоже, вам нужна аннотация, я бы посмотрел на источники (джексон - хороший кусок кода и легко понять) и беспокоит Тату Салоранту, который является автором - он обычно несет ответственность и живет где-то на западном побережье. –

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 

Чтобы получить более компактное представление, вам придется использовать пользовательскую сериализацию. Предполагая, что в вашем пространстве имен enum нет совпадений, вы можете сериализовать поле типа в качестве имени перечисления.

десериализатор нужно будет знать, какие типы доступны для строительства, либо путем обнаружения пути класса, или, как в следующем примере, просто жесткого кодирования ссылки:

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 
+0

Дело в том, что это класс, который я использую, это InsuredAmountA, из которых IAType является полем. Поэтому я думаю, что дополнение «.IATypeA» к сериализованному классу в моем случае не требуется. Но как я могу сказать это Джексону? –

+0

Итак, ваша цель - чистое представление json? Не могли бы вы добавить некоторые приемлемые примеры на ваш вопрос? – teppic

+0

Примеры приведены в прилагаемых тестовых таблицах: 'assertThat (json, is (" {\ "type \": \ "FOO \", \ "someInfo \": \ "test it \"} "))'. Вы ищете что-то конкретное? –

Смежные вопросы