java - 由于条件检查,使用 JsonParser 的杰克逊 JSON 反序列化正在跳过第一个键值对
问题描述
我正在使用 Jackson 反序列化 JSON。输入 JSON 可以是 2 种类型CustomerDocument
或Single Customer
. CustomerDocument
将有一个CustomerList
可以由大量的组成,Customer
而Single Customer
将只有一个Single Customer
。因此,杰克逊必须处理两件事:
- 识别提供的 JSON 是否为 a
CustomerDocument
,如果是则将元素CustomerList
逐个反序列化,而不是将整个内容存储到其中List
,以减少内存使用量。 - 确定提供的 JSON 是否为 a
single Customer
,如果是,则customer
直接反序列化。
我能够实现这一点,并且一切都按预期工作,但是当我提供时,CustomerDocument
它无法读取@Context
键值对,因为它已经在检查单人时被读取Customer
(正如您在第 2 点中提到的那样)。我想下面的代码会让问题变得清晰:
以下是我试图反序列化的 JSON:
{
"@context": [
"https://stackoverflow.com",
{
"example": "https://example.com"
}
],
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
以下是我的客户 POJO 类:
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer implements BaseResponse {
private String isA;
private String name;
private String age;
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer")})
interface BaseResponse {
}
以下是我的Main
课程,它将阅读JSON InputStream
并决定提供的输入 JSON 是CustomerList
还是Single Customer
然后相应地反序列化:
public class JacksonMain {
public static void main(String[] args) throws IOException {
final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customer.json");
final JsonParser jsonParser = new JsonFactory().createParser(jsonStream);
final ObjectMapper objectMapper = new ObjectMapper();
jsonParser.setCodec(objectMapper);
//Goto the start of the document
jsonParser.nextToken();
try {
BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class);
System.out.println("SINGLE EVENT INPUT" + baseResponse.toString());
} catch (Exception e) {
System.out.println("LIST OF CUSTOMER INPUT");
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
System.out.println("Current Token Name : " + jsonParser.getCurrentName());
if (jsonParser.getCurrentName() != null && jsonParser.getCurrentName().equalsIgnoreCase("@context")) {
System.out.println("WITHIN CONTEXT");
}
jsonParser.nextToken();
}
jsonParser.nextToken();
//Loop through each object within the customerList and deserilize them
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
final JsonNode customerNode = jsonParser.readValueAsTree();
final String eventType = customerNode.get("isA").asText();
Object event = objectMapper.treeToValue(customerNode, BaseResponse.class);
System.out.println(event.toString());
}
}
}
}
以下是我得到的输出:
LIST OF CUSTOMER INPUT
Current Token Name : isA
Customer(isA=Customer, name=Batman, age=2008)
正如我们所看到的,它只是在打印,Current Token Name: isA
我希望它能够打印isA
,@Context
因为它出现在isA
. 但我知道它没有打印,因为由于下面的代码行,它已经通过了try
:
BaseResponse baseResponse = objectMapper.readValue(jsonParser, BaseResponse.class);
以下是一个Single Customer
JSON(仅供参考,工作正常):
{
"@context": [
"https://stackoverflow.com",
{
"example": "https://example.com"
}
],
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
有人可以向我建议如何实现这一目标,是否有更好的解决方法来解决这个问题?
请注意:
CustomerList
可以有很多因此Customers
我不想将整个存储CustomerList
到一些List
中,因为它可能需要很多记忆。因此,我正在使用JsonParser
,所以我可以一次阅读一个JsonToken
。另外,我不想创建一个
CustomerList
类,而是想Customer
一次读取一个并反序列化它。JSON 结构无法修改,因为它来自另一个应用程序,它是我的应用程序的标准格式。
解决方案
我已经修复了代码,我删除了 catch 块。CUSTOMERS_LIST
属性区分单个和多个客户。
JacksonMain.java
public class JacksonMain {
private static final String CUSTOMERS_LIST = "customerList";
public static void main(String[] args) throws IOException {
final InputStream jsonStream = JacksonMain.class.getClassLoader().getResourceAsStream("Customers.json");
final JsonParser jsonParser = new JsonFactory().createParser(jsonStream);
final ObjectMapper objectMapper = new ObjectMapper();
jsonParser.setCodec(objectMapper);
TreeNode treeNode = objectMapper.readTree(jsonParser);
if (Objects.isNull(treeNode.get(CUSTOMERS_LIST))) {
BaseResponse baseResponse = objectMapper.treeToValue(treeNode, BaseResponse.class);
System.out.println("SINGLE EVENT INPUT: " + baseResponse);
} else {
System.out.println("LIST OF CUSTOMER INPUT");
ArrayNode customerList = (ArrayNode) treeNode.get(CUSTOMERS_LIST);
for (JsonNode node : customerList) {
BaseResponse event = objectMapper.treeToValue(node, BaseResponse.class);
System.out.println(event.toString());
}
}
}
}
我创建了两个资源 json 文件
客户.json
{
"@context": [
"https://stackoverflow.com",
{
"example": "https://example.com"
}
],
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
客户.json
{
"@context": [
"https://stackoverflow.com",
{
"example": "https://example.com"
}
],
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
客户文档.java
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
@ToString
public class CustomerDocument implements BaseResponse {
@JsonProperty
private String isA;
@JsonProperty(value="name")
private String name;
@JsonProperty(value="age")
private String age;
}
客户.java
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer implements BaseResponse {
private String isA;
private String name;
private String age;
}
BaseResponse.java
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer"),
@JsonSubTypes.Type(value = CustomerDocument.class, name = "CustomerDocument")})
@JsonIgnoreProperties(ignoreUnknown = true)
interface BaseResponse {
}
在界面中,我添加了 JsonIgnoreProperties 注释,并添加了一个错过的 JsonSubType。[客户文件]
这两种情况现在都在起作用。
性能测试:
Size: 1,065,065 records / customers handled
Time in seconds: 30.208
推荐阅读
- skype - Skype 按钮不出现
- sql - 基于 1 列旋转 2 列
- ruby-on-rails - db:migrate 中的 (Gem::UnsatisfiableDependencyError),仅在 Heroku 上?
- excel - 如何使用 VBA 在不同的工作表中列出一个列表
- java - 如何使用另一个类的 ActionListener 更改 JPanel 颜色?
- php - Laravel 对集合进行排序(分块)
- android - 无法在 com.google.firebase.database.DatabaseReference.child 的 child() 中为参数“pathString”传递 null(未知来源:40)
- python - 对数字列表进行所有可能的操作组合以查找特定数字
- javascript - 将这个和变量传递给导入的函数onclick
- python - 如何从列表中删除所有非字母?