英文:
Deserializing a complex Json in Java with Map Data Type
问题
以下是翻译好的内容:
我有一个通过HTTP请求解析的JSON对象。
{
"firstName": "User",
"lastName": "Test",
"emailId": "testnew@gmail.com",
"formsAndQuestions": {
"Form1": {
"Question1": {
"value": "NEVER",
"isBoolean": false
},
"Question2": {
"value": "YES"
}
},
"Form2": {
"Question1": {
"value": "OTHER"
}
}
}
}
表单对象可能是动态的。因此,我在我的Java类中使用以下变量对其进行反序列化。
Map<String, Map<String, Map<String, String>>> forms;
然后,我通过下面的复杂循环来迭代这些内容并读取答案值。
for (Map.Entry<String, Map<String, Map<String, String>>> entry : input.formsAndQuestions().get().entrySet()) {
logger.log("Form: " + entry.getKey());
for (Map.Entry<String, Map<String, String>> entry1 : entry.getValue().entrySet()) {
logger.log("Question: " + entry1.getKey());
for (Map.Entry<String, String> entry2 : entry1.getValue().entrySet()) {
logger.log("key: " + entry2.getKey());
logger.log("value: " + entry2.getValue());
}
}
}
是否有更好的方法来做这个?循环看起来相当复杂。我不想为反序列化JSON创建Java对象类,因为“formsAndQuestions”对象可以添加/删除多个表单,每个表单可以添加或删除多个问题/答案。
非常感谢您对这种方法的任何反馈。
英文:
I have the following JSON object that is parsed through a HTTP request.
{
"firstName": "User",
"lastName": "Test",
"emailId": "testnew@gmail.com",
"formsAndQuestions": {
"Form1": {
"Question1": {
"value": "NEVER",
"isBoolean": false
},
"Question2": {
"value": "YES"
}
},
"Form2": {
"Question1": {
"value": "OTHER"
}
}
}
}
The form object can be dynamic. Therefore I'm deserializing this in my Java class with the following variable.
Map<String, Map<String, Map<String, String>>> forms;
Then I'm going through the complex loop below to iterate these and read the answer values.
for (Map.Entry<String, Map<String, Map<String, String>>> entry : input.formsAndQuestions().get().entrySet()) {
logger.log("Form: " + entry.getKey());
for (Map.Entry<String, Map<String, String>> entry1 : entry.getValue().entrySet()) {
logger.log("Question: " + entry1.getKey());
for (Map.Entry<String, String> entry2 : entry1.getValue().entrySet()) {
logger.log("key: " + entry2.getKey());
logger.log("value: " + entry2.getValue());
}
}
}
Is there a better way to do this? Loop seems pretty complex. I don't want to create a Java object class for deserializing the json as the "formsAndQuestions" object can have multiple forms added/removed with multiple questions/answers per form added or removed.
Would greatly appreciate any feedback on this approach.
答案1
得分: 2
尽管形式和问题各不相同,但结构是相同的。您可以在Java
中使用@JsonAnySetter
注解轻松建模此结构,以正确设置随机形式。请看下面的示例:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class FormsAndQuestionsApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Result result = mapper.readValue(jsonFile, Result.class);
result.getFormsAndQuestions().getForms().forEach(System.out::println);
}
}
@Data
@ToString
class Result {
private String firstName;
private String lastName;
private String emailId;
private FormsAndQuestions formsAndQuestions;
}
@Data
@ToString
class FormsAndQuestions {
private List<Form> forms = new ArrayList<>();
@JsonAnySetter
public void anySetter(String formName, Map<String, Question> questions) {
// update questions with keys (question's name)
questions.forEach((k, v) -> v.setQuestion(k));
forms.add(new Form(formName, questions.values()));
}
}
@Data
@ToString
@AllArgsConstructor
class Form {
private String name;
private Collection<Question> questions;
}
@Data
@ToString
class Question {
private String question;
private String value;
private Boolean isBoolean;
}
以上代码输出:
Form(name=Form1, questions=[Question(question=Question1, value=NEVER, isBoolean=false), Question(question=Question2, value=YES, isBoolean=null)])
Form(name=Form2, questions=[Question(question=Question1, value=OTHER, isBoolean=null)])
关键点是 public void anySetter(String formName, Map<String, Question> questions)
方法。使用 @JsonAnySetter
,我们将 JSON 对象
转换为列表,因为对于每个未知的 键值对
,此方法将被调用。此外,我们将内部对象转换为适当的 Question
POJO
。value
字段由 Jackson
设置,我们只需设置 question
属性。现在,遍历此列表并在某些业务逻辑中稍后使用应该更加容易。
英文:
Even so forms and questions vary, the structure is the same. You can easily model this in Java
using @JsonAnySetter
annotation to set properly random forms. Take a look on below example:
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class FormsAndQuestionsApp {
public static void main(String[] args) throws IOException {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
Result result = mapper.readValue(jsonFile, Result.class);
result.getFormsAndQuestions().getForms().forEach(System.out::println);
}
}
@Data
@ToString
class Result {
private String firstName;
private String lastName;
private String emailId;
private FormsAndQuestions formsAndQuestions;
}
@Data
@ToString
class FormsAndQuestions {
private List<Form> forms = new ArrayList<>();
@JsonAnySetter
public void anySetter(String formName, Map<String, Question> questions) {
// update questions with keys (question's name)
questions.forEach((k, v) -> v.setQuestion(k));
forms.add(new Form(formName, questions.values()));
}
}
@Data
@ToString
@AllArgsConstructor
class Form {
private String name;
private Collection<Question> questions;
}
@Data
@ToString
class Question {
private String question;
private String value;
private Boolean isBoolean;
}
Above code prints:
Form(name=Form1, questions=[Question(question=Question1, value=NEVER, isBoolean=false), Question(question=Question2, value=YES, isBoolean=null)])
Form(name=Form2, questions=[Question(question=Question1, value=OTHER, isBoolean=null)])
The key point is public void anySetter(String formName, Map<String, Question> questions)
method. Using @JsonAnySetter
we convert JSON Object
to a list, because for every unknown key-value
pair this method will be invoked. Also, we convert inner object to proper Question
POJO
. value
field is set by Jackson
we need to set question
property only. Right now, it should be much easier to traverse this list and use later in some business logic.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论