2016-03-24 5 views
1

Ich versuche, eine Möglichkeit zu finden, die polymorphe Deserialisierung von jackson in einer Weise zu verwenden, dass es mein Objekt basierend auf einer Eigenschaft, die in Header verschachtelt ist deserialize/Steuerobjekt:Jackson polymorphe Deserialisierung mit Typ-Eigenschaft, die in Objekt geschachtelt ist

JSON 1 - CATEGORY1:

{ 
"id":"someId", 
"header":{ 
      "category":"CATEGORY1", 
      "somOtherProperty":"someValue" 
      } 
"nextField":"nextValue", 
... 
} 

JSON 2 - CATEGORY2

{ 
"id":"someId", 
"header":{ 
      "category":"CATEGORY2", 
      "somOtherProperty":"someValue" 
      } 
"nextField":"nextValue", 
... 
} 

Geordnete Klasse (Anmerkungen so etwas wie dieses)

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "category") 
@JsonSubTypes({ 
     @Type(value = Category1Class.class, name = "CATEGORY1"), 
     @Type(value = Category2Class.class, name = "CATEGORY2") }) 
public class ParentClass{ 
    private Header header; 
    private String nextField; 
    ... 
} 

public class Header{ 
    private String category; 
    private String somOtherProperty; 
    ... 
} 

Kinderklassen

@JsonTypeName("CATEGORY1") 
public class Category1Class extends ParentClass{ 
    ... 
} 

@JsonTypeName("CATEGORY2") 
public class Category2Class extends ParentClass{ 
    ... 
} 

ein aus der Box Funktionalität in Jackson ist es, die mir ermöglichen würde, diese Art von Deserialisierung zu tun, oder ich etwas fehle?

+0

sehen https://www.thomaskeller.biz/blog/2013/09/10/custom -polymorphe Art-Handhabung-mit-Jackson / –

Antwort

1

Wenn Sie auf die Jackson Api AsPropertyTypeDeserializer schauen, ist die Klasse verantwortlich für Sub-Typ-Identifizierung mit Eigenschaft. Wenn Sie sich diese Klasse ansehen, gibt es eine Methode namens deserializeTypedFromObject, die die Unterklasse mit identifiziert. Wir können diese Klasse erweitern und die Methoden deserializeTypedFromObject und überschreiben.

package com.dilipkumarg.tutorials.dynamicsubtype; 

import java.io.IOException; 

import com.fasterxml.jackson.core.JsonParser; 
import com.fasterxml.jackson.databind.BeanProperty; 
import com.fasterxml.jackson.databind.DeserializationContext; 
import com.fasterxml.jackson.databind.JavaType; 
import com.fasterxml.jackson.databind.JsonDeserializer; 
import com.fasterxml.jackson.databind.JsonNode; 
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 
import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; 
import com.fasterxml.jackson.databind.jsontype.impl.AsPropertyTypeDeserializer; 
import com.fasterxml.jackson.databind.node.TreeTraversingParser; 
import com.fasterxml.jackson.databind.type.SimpleType; 

public class CustomTypeDeserializer extends AsPropertyTypeDeserializer { 
public CustomTypeDeserializer(
     final JavaType bt, final TypeIdResolver idRes, 
     final String typePropertyName, final boolean typeIdVisible, final Class<?> defaultImpl) { 
    super(bt, idRes, typePropertyName, typeIdVisible, defaultImpl); 
} 

public CustomTypeDeserializer(
     final AsPropertyTypeDeserializer src, final BeanProperty property) { 
    super(src, property); 
} 

@Override 
public TypeDeserializer forProperty(
     final BeanProperty prop) { 
    return (prop == _property) ? this : new CustomTypeDeserializer(this, prop); 
} 

@Override 
public Object deserializeTypedFromObject(
     final JsonParser jp, final DeserializationContext ctxt) throws IOException { 
    JsonNode node = jp.readValueAsTree(); 
    Class<?> subType = findSubType(node); 
    JavaType type = SimpleType.construct(subType); 

    JsonParser jsonParser = new TreeTraversingParser(node, jp.getCodec()); 
    if (jsonParser.getCurrentToken() == null) { 
     jsonParser.nextToken(); 
    } 
    /* 16-Dec-2010, tatu: Since nominal type we get here has no (generic) type parameters, 
    * we actually now need to explicitly narrow from base type (which may have parameterization) 
    * using raw type. 
    * 
    * One complication, though; can not change 'type class' (simple type to container); otherwise 
    * we may try to narrow a SimpleType (Object.class) into MapType (Map.class), losing actual 
    * type in process (getting SimpleType of Map.class which will not work as expected) 
    */ 
    if (_baseType != null && _baseType.getClass() == type.getClass()) { 
     type = _baseType.narrowBy(type.getRawClass()); 
    } 
    JsonDeserializer<Object> deser = ctxt.findContextualValueDeserializer(type, _property); 
    return deser.deserialize(jsonParser, ctxt); 
} 

protected Class<?> findSubType(JsonNode node) { 
    Class<? extends ParentClass> subType = null; 
    String cat = node.get("header").get("category").asText(); 
    if (cat.equals("CATEGORY1")) { 
     subType = Category1Class.class; 
    } else if (cat.equals("CATEGORY2")) { 
     subType = Category2Class.class; 
    } 
    return subType; 
} 
} 

In erweiterten Klasse umgangen wir Untertypidentifikation unter Verwendung idResolver statt wir werden dynamisch mit category Bereich header Feld identifiziert.
Wir benötigen TypeResolverBuilder, um neue CustomTypeDeserializer Instanz zu erstellen. Jetzt

package com.dilipkumarg.tutorials.dynamicsubtype; 

import java.util.Collection; 

import com.fasterxml.jackson.databind.DeserializationConfig; 
import com.fasterxml.jackson.databind.JavaType; 
import com.fasterxml.jackson.databind.jsontype.NamedType; 
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; 
import com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder; 


public class CustomTypeResolver extends StdTypeResolverBuilder { 
    @Override 
    public TypeDeserializer buildTypeDeserializer(final DeserializationConfig config, final JavaType baseType, final Collection<NamedType> subtypes) { 
     return new CustomTypeDeserializer(baseType, null, 
      _typeProperty, _typeIdVisible, _defaultImpl); 
    } 
} 

wir haben eine CustomTypeResolver für Untertyperkennung, aber wie Jackon wissen diese Klasse zu suchen, wenn es ‚Parent‘ gefunden?
Wir können es auf zwei Arten tun:

  1. JackonAnnotationInterceptor mit benutzerdefinierter Konfiguration erweitern und konfigurieren, während ObjectMapper zu schaffen.

  2. Mit @JsonTypeResolver Annotation. Dies ist der empfohlene Ansatz, da wir nichts konfigurieren müssen.

Nach Art einschließlich Resolver neue ParentClass Klasse sein wird:

package com.dilipkumarg.tutorials.dynamicsubtype; 

import com.fasterxml.jackson.annotation.JsonTypeInfo; 
import com.fasterxml.jackson.databind.annotation.JsonTypeResolver; 

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE) 
@JsonTypeResolver(CustomTypeResolver.class) 
public class ParentClass { 
    private Header header; 
    private String nextField; 
    ... 
} 

siehe here

Verwandte Themen