Jackson - jak przetwarzać (deserializować) zagnieżdżony JSON?

{
  vendors: [
    {
      vendor: {
        id: 367,
        name: "Kuhn-Pollich",
        company_id: 1,
      }
    },
    {
      vendor: {
        id: 374,
        name: "Sawayn-Hermann",
        company_id: 1,
      }
  }]
}

Mam obiekt Vendor, który można właściwie deserializować z jednego "vendor" json, ale chcę deserializować to do Vendor[], po prostu nie mogę wymyślić, jak zmusić Jacksona do współpracy. Jakieś wskazówki?

Author: Sam Stern, 2012-07-31

3 answers

Twoje dane są problematyczne, ponieważ masz wewnętrzne wrapper obiekty w tablicy. Prawdopodobnie twój Vendor obiekt jest przeznaczony do obsługi id, name, company_id, ale każdy z tych wielu obiektów jest również zawinięty w obiekt z jedną właściwością vendor.

Zakładam, że używasz Jacksona powiązanie danych model.

Jeśli tak, to są dwie rzeczy do rozważenia:

Pierwszy to użycie specjalnej właściwości Jackson config. Jackson - od 1.9 uważam, że to może nie być dostępne, jeśli używasz starej wersji Jacksona-zapewnia UNWRAP_ROOT_VALUE. Jest przeznaczony do przypadków, w których wyniki są zawinięte w obiekt jednowarstwowy najwyższego poziomu, który chcesz odrzucić.

Więc pobaw się z:

objectMapper.configure(SerializationConfig.Feature.UNWRAP_ROOT_VALUE, true);

Drugi to użycie obiektów wrappera. Nawet po odrzuceniu zewnętrznego obiektu wrappera nadal występuje problem, że obiekty Vendor są owinięte w obiekt o pojedynczej właściwości. Użyj owijki, aby uzyskać wokół tego:

class VendorWrapper
{
    Vendor vendor;

    // gettors, settors for vendor if you need them
}

Podobnie, zamiast używać UNWRAP_ROOT_VALUES, można również zdefiniować klasę wrapper do obsługi zewnętrznego obiektu. Zakładając, że masz poprawne Vendor, VendorWrapper obiekt, można zdefiniować:

class VendorsWrapper
{
    List<VendorWrapper> vendors = new ArrayList<VendorWrapper>();

    // gettors, settors for vendors if you need them
}

// in your deserialization code:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readValue(jsonInput, VendorsWrapper.class); 

Drzewo obiektów dla VendorsWrapper jest analogiczne do twojego JSON:

VendorsWrapper:
    vendors:
    [
        VendorWrapper
            vendor: Vendor,
        VendorWrapper:
            vendor: Vendor,
        ...
    ]

Wreszcie możesz użyć Jacksona Model drzewa w tym celu należy wykonać następujące czynności:]}

mapper.readValue(node.get("vendor").getTextValue(), Vendor.class);

Że może skutkować mniejszą ilością kodu, ale wydaje się nie mniej niezdarny niż użycie dwóch opakowań.

 24
Author: pb2q,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-01 22:36:01

Oto przybliżone, ale bardziej deklaratywne rozwiązanie. Nie udało mi się sprowadzić tego do jednej adnotacji, ale to chyba działa dobrze. Również nie jestem pewien wydajności na dużych zestawach danych.

BiorÄ…c pod uwagÄ™ ten JSON:

{
    "list": [
        {
            "wrapper": {
                "name": "Jack"
            }
        },
        {
            "wrapper": {
                "name": "Jane"
            }
        }
    ]
}

I te obiekty modelu:

public class RootObject {
    @JsonProperty("list")
    @JsonDeserialize(contentUsing = SkipWrapperObjectDeserializer.class)
    @SkipWrapperObject("wrapper")
    public InnerObject[] innerObjects;
}

I

public class InnerObject {
    @JsonProperty("name")
    public String name;
}

Gdzie Jackson voodoo jest zaimplementowany w następujący sposób:

@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface SkipWrapperObject {
    String value();
}

I

public class SkipWrapperObjectDeserializer extends JsonDeserializer<Object> implements
        ContextualDeserializer {
    private Class<?> wrappedType;
    private String wrapperKey;

    public JsonDeserializer<?> createContextual(DeserializationContext ctxt,
            BeanProperty property) throws JsonMappingException {
        SkipWrapperObject skipWrapperObject = property
                .getAnnotation(SkipWrapperObject.class);
        wrapperKey = skipWrapperObject.value();
        JavaType collectionType = property.getType();
        JavaType collectedType = collectionType.containedType(0);
        wrappedType = collectedType.getRawClass();
        return this;
    }

    @Override
    public Object deserialize(JsonParser parser, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode objectNode = mapper.readTree(parser);
        JsonNode wrapped = objectNode.get(wrapperKey);
        Object mapped = mapIntoObject(wrapped);
        return mapped;
    }

    private Object mapIntoObject(JsonNode node) throws IOException,
            JsonProcessingException {
        JsonParser parser = node.traverse();
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(parser, wrappedType);
    }
}

Mam nadzieję, że jest to przydatne dla kogoś!

 30
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-26 06:10:27

@ Patryk Poprawiłbym nieco Twoje rozwiązanie

@Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {        
    ObjectNode objectNode = jp.readValueAsTree();
    JsonNode wrapped = objectNode.get(wrapperKey);
    JsonParser parser = node.traverse();
    parser.setCodec(jp.getCodec());
    Vendor mapped = parser.readValueAs(Vendor.class);
    return mapped;
}

Działa szybciej:)

 8
Author: Alexey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-29 16:08:55