[英]Jackson Parse JSON Object as Array of Objects
我正在嘗試在Jackson中解析以下JSON:
{
"x:y" : 1,
"x:z" : 2,
"u:v" : 3,
// Several dynamically generated entries...
}
數據以這種方式格式化,並且超出了我的控制范圍。 條目是動態的,但始終采用以下形式:
"first:second" : value
我一直在嘗試將其序列化為容器類:
private static class MyClass {
String first;
String second;
Number value;
@JsonCreator
public MyClass(@JsonProperty("both") String both, @JsonProperty("value") Number value) {
String[] split = both.split(":");
first = split[0];
second = split[1];
this.value = value;
}
}
但是我得到一個錯誤:
線程“主”中的異常com.fasterxml.jackson.databind.exc.MismatchedInputException:無法從START_OBJECT令牌中反序列化entry.JacksonObjectTest $ MyClass []
我感覺合理; 我正在嘗試將JSON對象的每個字段解析為對象數組,而Jackson顯然對此不太滿意。 忽略@JsonProperty("both")
產生:
線程“主”中的異常com.fasterxml.jackson.databind.exc.InvalidDefinitionException:類型條目的類型定義無效。JacksonObjectTest$ MyClass:參數0沒有屬性名,不可注入:不能用作Creator [條目的構造方法.JacksonObjectTest $ MyClass,注釋:{interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml.jackson.annotation.JsonCreator(mode = DEFAULT)})
這對我也很有意義; 它不知道如何解析此構造函數(這實際上與上面的問題相同;我放入批注只是用另一個錯誤掩蓋了該錯誤)。
所以我的問題是; 我如何讓傑克遜了解我想要的東西?
MCVE :
public class JacksonObjectTest {
public static void main(String[] args) throws IOException {
String data = "{\"x:y\":1,\"x:z\":2,\"u:v\":3}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(data);
MyClass[] out = mapper.readValue(node.traverse(), MyClass[].class);
System.out.println(out);
}
private static class MyClass {
String first;
String second;
Number value;
@JsonCreator
public MyClass(@JsonProperty("both") String both, @JsonProperty("value") Number value) {
String[] split = both.split(":");
first = split[0];
second = split[1];
this.value = value;
}
}
}
編輯 :如評論中所述,我確實知道使用TypeReference<Map<String,Number>>
。 這可行,但是我試圖使解析代碼盡可能地包含和通用,並且使用此解決方案意味着我必須在解析后做進一步的轉換以獲得MyClass[]
(對Map<String,Number>
第一次解析,然后為MyClass[]
。 有沒有一種方法可以跳過中間人(即:告訴Jackson如何將已知格式的JSON斑點處理為數據類型)?
您可以使用JsonAnySetter
注釋來注釋用於讀取對象中所有屬性的方法:
class MultiNamedProperties {
private List<Property> properties = new ArrayList<>();
@JsonAnySetter
public void readProperty(String property, Number value) {
String[] names = property.split(":");
properties.add(new Property(names[0], names[1], value));
}
@Override
public String toString() {
return "MultiNamedProperties{" +
"properties=" + properties +
'}';
}
}
class Property {
private final String first;
private final String second;
private final Number value;
Property(String first, String second, Number value) {
this.first = first;
this.second = second;
this.value = value;
}
@Override
public String toString() {
return "MyClass{" +
"first='" + first + '\'' +
", second='" + second + '\'' +
", value=" + value +
'}';
}
}
您可以如下使用它:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws Exception {
String data = "{\"x:y\":1,\"x:z\":2,\"u:v\":3}";
ObjectMapper mapper = new ObjectMapper();
MultiNamedProperties mnp = mapper.readValue(data, MultiNamedProperties.class);
System.out.println(mnp);
}
}
上面的示例打印:
MultiNamedProperties{properties=[MyClass{first='x', second='y', value=1}, MyClass{first='x', second='z', value=2}, MyClass{first='u', second='v', value=3}]}
該解決方案僅需要一個注釋和兩個對象。
不確定jackson是否可以僅使用內置注釋和類來解析您的數據結構。 當我在解析json時需要添加邏輯時,我總是寫自己的解串器:
public void loadJsonObjectAsArray() throws IOException {
String data = "{\"x:y\":1,\"x:z\":2,\"u:v\":3}";
ObjectMapper mapper = new ObjectMapper();
Wrapper wrapper = mapper.readValue(data, Wrapper.class);
List<MyClass> out = wrapper.values;
System.out.println(out);
}
public static class WrapperDeserializer extends StdDeserializer<Wrapper> {
public WrapperDeserializer() {
this(null);
}
public WrapperDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Wrapper deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonNode node = jp.getCodec().readTree(jp);
ObjectNode obj = (ObjectNode) node;
List<MyClass> parsedFields = new ArrayList<>();
obj.fields().forEachRemaining(fieldAndNode -> {
String fieldName = fieldAndNode.getKey();
Number value = fieldAndNode.getValue().numberValue();
parsedFields.add(new MyClass(fieldName, value));
});
return new Wrapper(parsedFields);
}
}
private static class MyClass {
String first;
String second;
Number value;
public MyClass(String both, Number value) {
String[] split = both.split(":");
first = split[0];
second = split[1];
this.value = value;
}
@Override
public String toString() {
return "MyClass{" +
"first='" + first + '\'' +
", second='" + second + '\'' +
", value=" + value +
'}';
}
}
@JsonDeserialize(using = WrapperDeserializer.class)
private static class Wrapper {
private final List<MyClass> values;
public Wrapper(List<MyClass> values) {
this.values = new ArrayList<>(values);
}
}
編輯 :提供的其他答案可以更好地回答整個問題。 將保留此位置以為注釋中提到的解決方案提供參考(對於那些只想快速格式化數據的人來說可能更容易)。
這是我目前的解決方案。 我想指出,它沒有解決問題的編輯部分中提到的問題。
注釋中提到了使用的方法:首先解析為Map<String,Number>
,然后將其轉換為List<MyClass>
:
public class JacksonObjectTest {
public static void main(String[] args) throws IOException {
String data = "{\"x:y\":1,\"x:z\":2,\"u:v\":3}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(data);
// Note the difference in these two lines from the MCVE.
Map<String,Number> interim = mapper.readValue(node.traverse(), new TypeReference<Map<String,Number>>(){});
List<MyClass> out = interim.entrySet().stream().map(MyClass::new).collect(Collectors.toList());
System.out.println(out);
}
private static class MyClass {
String first;
String second;
Number value;
public MyClass(Entry<String, Number> entry) {
String[] split = entry.getKey().split(":");
first = split[0];
second = split[1];
value = entry.getValue();
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.