2015-03-11 11 views
8

Przypuszczałem, że miałem rodzic Class Parametr że ma 2 klasy sub ComboParameter i IntegerParameterJacksonPolymorphicDeserialization: JsonMappingException

@JsonSubTypes({ 
    @JsonSubTypes.Type(value = IntegerParameter.class, name = "integerParam"), 
    @JsonSubTypes.Type(value = ComboParameter.class, name = "comboParam") 
}) 
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT) 
public abstract class Parameter { 
String regEx; 
} 


@JsonTypeName("integerParam") 
public class IntegerParameter extends Parameter { 
} 

@JsonTypeName("comboParam") 
public class ComboParameter extends Parameter { 
List<String> values; 
} 

I mam klasę, która miała parametr atrybutu

class A { 
@JsonUnwrapped 
Parameter parameter; 
} 

szeregowanie obiektu A rzut wyjątek

com.fasterxml.jackson.databind.JsonMappingException: nieruchomość Unwrapped wymaga wykorzystania informacji typu: nie można szeregować bez wyłączania SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS

A jeśli usunę adnotacji @JsonUnwrapped będę miał json Like That

{ 
    parameter:{ 
      integerParam:{ 
       regEx: regExVal 
      } 
    } 
} 

A co muszę to json tak:

{ 
    integerParam:{ 
      regEx: regExVal 
    } 
} 

NB I używam Jacksona 2.4.4

+0

wygląd [Jackson polimorficznej deserializacjia] (http://wiki.fasterxml.com/JacksonPolymorphicDeserialization) – Rembo

+0

można znaleźć przykład [tutaj] (http://stackoverflow.com/a/6543330/1066779) – Rembo

+0

I edycja kwestia być jaśniejsze –

Odpowiedz

1

Nie sądzę, że istnieje proste i czyste rozwiązanie dla tego problemu. Ale tutaj jest kilka myśli w jaki sposób można go rozwiązać (Gist demo obu przypadkach):

Wariant 1: Dodaj @JsonIgnore powyżej własności i @JsonAnyGetter na najwyższym poziomie fasoli. Łatwe do wdrożenia, ale nie miło mieć statyczny ObjectMapper w fasoli i będzie musiał skopiować ten kod do każdego był z własności parametru

public class A { 

    @JsonIgnore 
    Parameter parameter; 

    // can put ObjectMapper and most of this code in util class 
    // and just use JacksonUtils.toMap(parameter) as return of JsonAnyGetter 

    private static ObjectMapper mapper = new ObjectMapper(); 

    /************************ Serialization ************************/ 

    @JsonAnyGetter 
    private Map<String, Object> parameterAsMap(){ 
     return mapper.convertValue(parameter, Map.class); 
    } 

    /************************ Deserialization **********************/ 

    @JsonAnySetter 
    private void parameterFromMap(String key, JsonNode value) { 
     try { 
      parameter = mapper.readValue(String.format("{\"%s\":%s}", key,value), 
        Parameter.class); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

Opcja 2:@JsonIgnore nieruchomości i zarejestrować niestandardowe serializatora/Deserializator root A Klasa

SimpleModule module = new SimpleModule(); 
module.addSerializer(A.class, new ASerializer()); 
module.addDeserializer(A.class, new ADeserializer()); 
mapper.registerModule(module); 

nie można użyć @JsonSerialize powyżej lekcje, bo serializers i deserializers wewnętrzna ObjectMapper będzie również korzystać z tej adnotacji, ale musisz ustawić go u se domyślny serializer/deserializer, a nie sam rekursywnie. Czy można zaimplementować coś podobnego https://stackoverflow.com/a/18405958/1032167 jeśli naprawdę chcesz adnotacji

I serializer + Deserializator będzie wyglądać mniej więcej tak (Niezoptymalizowana i tylko dowodem koncepcji):

/************************ Serialization ************************/ 

public static class ASerializer extends JsonSerializer<A> { 
    private static ObjectMapper m = new ObjectMapper(); 

    @Override 
    public void serialize(A value, JsonGenerator gen, 
          SerializerProvider serializers) throws IOException { 
     Map defaults = m.convertValue(value, Map.class); 
     Map params = m.convertValue(value.getParameter(), Map.class); 
     defaults.putAll(params); 
     gen.writeObject(defaults); 
    } 

} 

    /************************ Deserialization **********************/ 

public static class ADeserializer extends JsonDeserializer<A> { 
    private static ObjectMapper m = new ObjectMapper(); 
    private static String[] subtipes = {"integerParam", "comboParam"}; 

    public ADeserializer() { 
     m.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); 
    } 

    @Override 
    public A deserialize(JsonParser p, DeserializationContext ctxt) 
      throws IOException { 

     TreeNode node = m.readTree(p); 

     A a = m.convertValue(node, A.class); 

     // hardcoded , probably can be done dynamically 
     // with annotations inspection 
     for (String key : subtipes) { 
      TreeNode value = node.get(key); 
      if (value != null) { 
       String json = String.format("{\"%s\":%s}", key, value); 
       a.setParameter(m.readValue(json, Parameter.class)); 
       break; 
      } 
     } 

     return a; 
    } 
} 

Generic Deserializator byłoby dość trudne do napisania. Ale to pytanie dotyczy serializacji według ciała pytania.

Powiązane problemy