Jackson反序列化使用JsonParser区分直接对象和数组内对象
Jackson deserialization using JsonParser distinguish between the direct object and objects within array
我正在使用 Jackson
对 JSON 进行 Deseilization。 Deseilization
非常适合 JSON 和 CustomerDocument
。但是,我有一个新要求,我需要确定提供的 JSON 是否具有 CustomerDocument
或仅 Customer
.
我能够为两者开发逻辑,但问题是当我尝试合并时它不适用于 CustomerDocument
。我正在寻找对两者都适用的解决方案。我想做的就是建立逻辑来区分传入的 JSON 基于 customerDocument
和单个 Customer
.
以下是CustomerDocument
JSON:
{
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
Customer.class:
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer {
private String isA;
private String name;
private String age;
}
杰克逊主线:
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();
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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, Customer.class);
System.out.println(event.toString());
}
}
}
以上代码完美运行并产生以下结果:
Customer(isA=Customer, name=Batman, age=2008)
场景 2
现在用户可以提供没有 customerDocument
的直接 customer
对象。像这样:
{
"isA": "Customer",
"name": "Superman",
"age": "2013"
}
'Customer.class' 将保持不变,而 JacksonMain
将修改为:
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();
final JsonNode jsonNode = jsonParser.readValueAsTree();
final String inputType = jsonNode.get("isA").asText();
if (inputType.equalsIgnoreCase("Customer")) {
Object singleCustomer = objectMapper.treeToValue(jsonNode, Customer.class);
System.out.println(singleCustomer.toString());
} else if (inputType.equalsIgnoreCase("CustomerDocument")) {
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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, Customer.class);
System.out.println(event.toString());
}
}
}
}
对于单个 CUstomer
这将产生以下结果:
Customer(isA=Customer, name=Superman, age=2013)
现在对于相同的代码,如果我提供 CustomerDocument
(第一个 JSON),那么它将无法工作并因错误而失败:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "com.fasterxml.jackson.core.JsonParser.getText()" is null
at stackover.JacksonMain.main(JacksonMain.java:32)
我知道这个问题是因为这条线
final JsonNode jsonNode = jsonParser.readValueAsTree();
有人可以解释一下如何使用 Jackson 使代码同时适用于 JSON customerDocument
类型和单个 Customer
类型吗?我只是想区分传入JSON是customerDocument
还是单个Customer
。任何帮助将不胜感激。
- 我想使用 Jackson 来区分两个输入。
- 如果不需要创建任何
additional classes
就好了。但是,如果需要创建一个 interface
来实现这一点也没关系。
- 我的
CustomerList
可能非常大,所以我正在一张一张地阅读,所以不会占用太多内存。因此我没有 CustomerDocument
class 和 List<Customer>
而是我正在查看它并一张一张地映射。
你可以使用 Jackson 子类型在 Customer
和 CustomerDocument
之间反序列化。
类似以下内容,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
BaseResponse baseResponse = om.readValue(s, BaseResponse.class);
if (baseResponse instanceof CustomerDocument) {
CustomerDocument cd = (CustomerDocument) baseResponse;
System.out.println("Inside If..");
cd.getCustomerList().forEach(System.out::println);
} else if (baseResponse instanceof Customer) {
System.out.println("Inside Else If..");
Customer cs = (Customer) baseResponse;
System.out.println(cs);;
}
}
}
@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")})
interface BaseResponse {}
@Getter
@Setter
@ToString
class Customer implements BaseResponse{
private String isA;
private String name;
private String age;
}
@Getter
@Setter
@ToString
class CustomerDocument implements BaseResponse{
private String isA;
private List<Customer> customerList;
}
PS - 取消注释 main
方法中的字符串以说明另一种情况。
更新
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"},{\"isA\":\"Customer B\",\"name\":\"Superman\",\"age\":\"2013\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
JsonNode node = om.readTree(s);
String type = node.get("isA").asText();
if (type.equals("Customer")) {
Customer c = om.readValue(s, Customer.class);
System.out.println(c);
} else if (type.equals("CustomerDocument")) {
JsonNode nextNode = node.path("customerList");
List<Customer> cl = om.convertValue(nextNode, new TypeReference<List<Customer>>() {});
cl.forEach(System.out::println);
}
}
}
@Getter
@Setter
@ToString
class Customer {
private String isA;
private String name;
private String age;
}
根据以上内容为我工作的以下人提供了答案:
BaseResponse 接口:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer")})
public interface BaseResponse {
}
客户class:
@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;
}
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");
System.out.println(baseResponse.toString());
} catch (Exception e) {
System.out.println("LIST OF CUSTOMER INPUT");
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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());
}
}
}
}
我正在使用 Jackson
对 JSON 进行 Deseilization。 Deseilization
非常适合 JSON 和 CustomerDocument
。但是,我有一个新要求,我需要确定提供的 JSON 是否具有 CustomerDocument
或仅 Customer
.
我能够为两者开发逻辑,但问题是当我尝试合并时它不适用于 CustomerDocument
。我正在寻找对两者都适用的解决方案。我想做的就是建立逻辑来区分传入的 JSON 基于 customerDocument
和单个 Customer
.
以下是CustomerDocument
JSON:
{
"isA": "CustomerDocument",
"customerList": [
{
"isA": "Customer",
"name": "Batman",
"age": "2008"
}
]
}
Customer.class:
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer {
private String isA;
private String name;
private String age;
}
杰克逊主线:
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();
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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, Customer.class);
System.out.println(event.toString());
}
}
}
以上代码完美运行并产生以下结果:
Customer(isA=Customer, name=Batman, age=2008)
场景 2
现在用户可以提供没有 customerDocument
的直接 customer
对象。像这样:
{
"isA": "Customer",
"name": "Superman",
"age": "2013"
}
'Customer.class' 将保持不变,而 JacksonMain
将修改为:
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();
final JsonNode jsonNode = jsonParser.readValueAsTree();
final String inputType = jsonNode.get("isA").asText();
if (inputType.equalsIgnoreCase("Customer")) {
Object singleCustomer = objectMapper.treeToValue(jsonNode, Customer.class);
System.out.println(singleCustomer.toString());
} else if (inputType.equalsIgnoreCase("CustomerDocument")) {
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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, Customer.class);
System.out.println(event.toString());
}
}
}
}
对于单个 CUstomer
这将产生以下结果:
Customer(isA=Customer, name=Superman, age=2013)
现在对于相同的代码,如果我提供 CustomerDocument
(第一个 JSON),那么它将无法工作并因错误而失败:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "com.fasterxml.jackson.core.JsonParser.getText()" is null
at stackover.JacksonMain.main(JacksonMain.java:32)
我知道这个问题是因为这条线
final JsonNode jsonNode = jsonParser.readValueAsTree();
有人可以解释一下如何使用 Jackson 使代码同时适用于 JSON customerDocument
类型和单个 Customer
类型吗?我只是想区分传入JSON是customerDocument
还是单个Customer
。任何帮助将不胜感激。
- 我想使用 Jackson 来区分两个输入。
- 如果不需要创建任何
additional classes
就好了。但是,如果需要创建一个interface
来实现这一点也没关系。 - 我的
CustomerList
可能非常大,所以我正在一张一张地阅读,所以不会占用太多内存。因此我没有CustomerDocument
class 和List<Customer>
而是我正在查看它并一张一张地映射。
你可以使用 Jackson 子类型在 Customer
和 CustomerDocument
之间反序列化。
类似以下内容,
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
BaseResponse baseResponse = om.readValue(s, BaseResponse.class);
if (baseResponse instanceof CustomerDocument) {
CustomerDocument cd = (CustomerDocument) baseResponse;
System.out.println("Inside If..");
cd.getCustomerList().forEach(System.out::println);
} else if (baseResponse instanceof Customer) {
System.out.println("Inside Else If..");
Customer cs = (Customer) baseResponse;
System.out.println(cs);;
}
}
}
@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")})
interface BaseResponse {}
@Getter
@Setter
@ToString
class Customer implements BaseResponse{
private String isA;
private String name;
private String age;
}
@Getter
@Setter
@ToString
class CustomerDocument implements BaseResponse{
private String isA;
private List<Customer> customerList;
}
PS - 取消注释 main
方法中的字符串以说明另一种情况。
更新
public class Main {
public static void main(String[] args) throws IOException {
String s = "{\"isA\":\"CustomerDocument\",\"customerList\":[{\"isA\":\"Customer\",\"name\":\"Batman\",\"age\":\"2008\"},{\"isA\":\"Customer B\",\"name\":\"Superman\",\"age\":\"2013\"}]}";
// String s = "{\"isA\":\"Customer\",\"name\":\"Superman\",\"age\":\"2013\"}";
ObjectMapper om = new ObjectMapper();
JsonNode node = om.readTree(s);
String type = node.get("isA").asText();
if (type.equals("Customer")) {
Customer c = om.readValue(s, Customer.class);
System.out.println(c);
} else if (type.equals("CustomerDocument")) {
JsonNode nextNode = node.path("customerList");
List<Customer> cl = om.convertValue(nextNode, new TypeReference<List<Customer>>() {});
cl.forEach(System.out::println);
}
}
}
@Getter
@Setter
@ToString
class Customer {
private String isA;
private String name;
private String age;
}
根据以上内容为我工作的以下人提供了答案:
BaseResponse 接口:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonSubTypes({
@JsonSubTypes.Type(value = Customer.class, name = "Customer")})
public interface BaseResponse {
}
客户class:
@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;
}
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");
System.out.println(baseResponse.toString());
} catch (Exception e) {
System.out.println("LIST OF CUSTOMER INPUT");
//Go until the customerList has been reached
while (!jsonParser.getText().equals("customerList")) {
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());
}
}
}
}