一個簡單的解決方案:而不是直接反序列化它作爲一個數組/列表,它反序列化到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);
你想保留數組中的索引還是僅僅是排序?即你期望'result [0]'是第一個條目還是'result [1]'? – araqnid
@araqnid @araqnid我想解析數據在訂購,使用傑克遜 –