簡體   English   中英

使用Jackson解析以數字為鍵的JSON數組?

[英]Parsing JSON Array with numbers as keys using Jackson?

如何使用Jackson解析以下種類的JSON數組並保留內容的順序:

{
  "1": {
    "title": "ABC",
    "category": "Video",
  },
  "2": {
    "title": "DEF",
    "category": "Audio",
  },
  "3": {
    "title": "XYZ",
    "category": "Text",
  }
}

一個簡單的解決方案:與其直接將其反序列化為數組/列表,反而將其反序列化為SortedMap<Integer, Value> ,然后在其上調用values()以按順序獲取值。 有點混亂,因為它公開了模型對象中JSON處理的詳細信息,但這是最少的實現工作。

@Test
public void deserialize_object_keyed_on_numbers_as_sorted_map() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    SortedMap<Integer, Value> container = mapper
            .reader(new TypeReference<SortedMap<Integer, Value>>() {})
            .with(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
            .with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
            .readValue(
                    "{ 1: { title: 'ABC', category: 'Video' }, 2: { title: 'DEF', category: 'Video' }, 3: { title: 'XYZ', category: 'Video' } }");
    assertThat(container.values(),
            contains(new Value("ABC", "Video"), new Value("DEF", "Video"), new Value("XYZ", "Video")));
}


public static final class Value {
    public final String title;
    public final String category;

    @JsonCreator
    public Value(@JsonProperty("title") String title, @JsonProperty("category") String category) {
        this.title = title;
        this.category = category;
    }
}

但是,如果您只想在模型中包含Collection<Value>並將其隱藏起來,則可以創建一個自定義反序列化器來執行此操作。 請注意,您需要為反序列化器實現“上下文化”:它將需要知道集合中對象的類型。 (盡管只有一種情況,您可以對它進行硬編碼,但是我覺得這有什么意思呢?)

@Test
public void deserialize_object_keyed_on_numbers_as_ordered_collection() throws Exception {
    ObjectMapper mapper = new ObjectMapper();
    CollectionContainer container = mapper
            .reader(CollectionContainer.class)
            .with(JsonParser.Feature.ALLOW_SINGLE_QUOTES)
            .with(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
            .readValue(
                    "{ values: { 1: { title: 'ABC', category: 'Video' }, 2: { title: 'DEF', category: 'Video' }, 3: { title: 'XYZ', category: 'Video' } } }");
    assertThat(
            container,
            equalTo(new CollectionContainer(ImmutableList.of(new Value("ABC", "Video"), new Value("DEF", "Video"),
                    new Value("XYZ", "Video")))));
}


public static final class CollectionContainer {
    @JsonDeserialize(using = CustomCollectionDeserializer.class)
    public final Collection<Value> values;

    @JsonCreator
    public CollectionContainer(@JsonProperty("values") Collection<Value> values) {
        this.values = ImmutableList.copyOf(values);
    }
}

(請注意,為便於閱讀,省略了hashCode()equals(x)等的定義)

最后是解串器實現:

public static final class CustomCollectionDeserializer extends StdDeserializer<Collection<?>> implements
        ContextualDeserializer {
    private JsonDeserializer<Object> contentDeser;

    public CustomCollectionDeserializer() {
        super(Collection.class);
    }

    public CustomCollectionDeserializer(JavaType collectionType, JsonDeserializer<Object> contentDeser) {
        super(collectionType);
        this.contentDeser = contentDeser;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property)
            throws JsonMappingException {
        if (!property.getType().isCollectionLikeType()) throw ctxt
                .mappingException("Can only be contextualised for collection-like types (was: "
                        + property.getType() + ")");
        JavaType contentType = property.getType().getContentType();
        return new CustomCollectionDeserializer(property.getType(), ctxt.findContextualValueDeserializer(
                contentType, property));
    }

    @Override
    public Collection<?> deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
        if (contentDeser == null) throw ctxt.mappingException("Need context to produce elements of collection");
        SortedMap<Integer, Object> values = new TreeMap<>();
        for (JsonToken t = p.nextToken(); t != JsonToken.END_OBJECT; t = p.nextToken()) {
            if (t != JsonToken.FIELD_NAME) throw ctxt.wrongTokenException(p, JsonToken.FIELD_NAME,
                    "Expected index field");
            Integer index = Integer.valueOf(p.getText());
            p.nextToken();
            Object value = contentDeser.deserialize(p, ctxt);
            values.put(index, value);
        }
        return values.values();
    }
}

至少涵蓋了這種簡單情況:諸如集合內容為多態類型之類的事情可能需要更多處理:請參閱Jackson自己的CollectionDeserializer的來源。

另外,如果未提供上下文,則可以將UntypedObjectDeserializer用作默認值,而不是阻塞。

最后,如果您希望解串器返回保留索引的List,則可以修改上面的內容,然后插入TreeMap的一些后處理:

        int capacity = values.lastKey() + 1;
        Object[] objects = new Object[capacity];
        values.forEach((key, value) -> objects[key] = value);
        return Arrays.asList(objects);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM