反序列化复杂的 JSON 在 Java 中使用 Map 数据类型。

huangapple go评论71阅读模式
英文:

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.

{
 &quot;firstName&quot;: &quot;User&quot;,
 &quot;lastName&quot;: &quot;Test&quot;,
 &quot;emailId&quot;: &quot;testnew@gmail.com&quot;,
 &quot;formsAndQuestions&quot;: {
   &quot;Form1&quot;: {
      &quot;Question1&quot;: {
        &quot;value&quot;: &quot;NEVER&quot;,
       &quot;isBoolean&quot;: false
      },
      &quot;Question2&quot;: {
        &quot;value&quot;: &quot;YES&quot;
      }
    },
    &quot;Form2&quot;: {
      &quot;Question1&quot;: {
        &quot;value&quot;: &quot;OTHER&quot;
      }
    }
  }
}

The form object can be dynamic. Therefore I'm deserializing this in my Java class with the following variable.

Map&lt;String, Map&lt;String, Map&lt;String, String&gt;&gt;&gt; forms;

Then I'm going through the complex loop below to iterate these and read the answer values.

for (Map.Entry&lt;String, Map&lt;String, Map&lt;String, String&gt;&gt;&gt; entry : input.formsAndQuestions().get().entrySet()) {
        logger.log(&quot;Form: &quot; + entry.getKey());
        for (Map.Entry&lt;String, Map&lt;String, String&gt;&gt; entry1 : entry.getValue().entrySet()) {
            logger.log(&quot;Question: &quot; + entry1.getKey());
            for (Map.Entry&lt;String, String&gt; entry2 : entry1.getValue().entrySet()) {
                logger.log(&quot;key: &quot; + entry2.getKey());
                logger.log(&quot;value: &quot; + 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 POJOvalue 字段由 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(&quot;./resource/test.json&quot;).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&lt;Form&gt; forms = new ArrayList&lt;&gt;();
@JsonAnySetter
public void anySetter(String formName, Map&lt;String, Question&gt; questions) {
// update questions with keys (question&#39;s name)
questions.forEach((k, v) -&gt; v.setQuestion(k));
forms.add(new Form(formName, questions.values()));
}
}
@Data
@ToString
@AllArgsConstructor
class Form {
private String name;
private Collection&lt;Question&gt; 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&lt;String, Question&gt; 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.

huangapple
  • 本文由 发表于 2020年8月28日 04:34:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/63623806.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定