2015-10-24 50 views
1

如何分析以下使用傑克遜保持了內容的順序樣JSON陣列:用數字解析JSON數組作爲使用Jackson的鍵?

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

你想保留數組中的索引還是僅僅是排序?即你期望'result [0]'是第一個條目還是'result [1]'? – araqnid

+0

@araqnid @araqnid我想解析數據在訂購,使用傑克遜 –

回答

2

一個簡單的解決方案:而不是直接反序列化它作爲一個數組/列表,它反序列化到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>,並隱藏這個細節的時候,你可以創建自定義解串器來做到這一點。請注意,您需要爲反序列化器實現「contextualisation」:它需要知道集合中對象的類型。 (雖然你可以硬編碼此,如果你只有它的一個情況下,我想,哪來的樂趣呢?)

@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(); 
    } 
} 

這至少覆蓋這個簡單的例子:東西,如託收態的類型可能需要更多處理的內容:看傑克遜的流量來源CollectionDeserializer。

此外,如果沒有給出上下文,您可以使用UntypedObjectDeserializer作爲默認值而不是窒息。

最後,如果你想解串器返回與保存索引列表,你可以修改上面的,只是插入位映像樹的後處理的:

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

我無法理解你的答案...你可以讓它容易一點,可能你正在回答與更復雜的東西.. –

+0

可能只是使用第一答案中的建議:這將把輸入字符串解析爲一個'SortedMap ',然後你可以調用'values()'來獲得值,而不必處理解串器等。可以縮短爲像'SortedMap map = mapper.readValue(input,new TypeReference >(){})' – araqnid

+0

對於.reader(new TypeReference