【问题标题】:How to ignore a specific field while parsing a JSON into map如何在将 JSON 解析为地图时忽略特定字段
【发布时间】:2015-11-18 16:29:06
【问题描述】:

我想将下面的 JSON 解析成 POJO。我正在使用jackson来解析json。

{
  "totalSize": 4,
  "done": true,
  "records": [
    {
      "attributes": {
        "type": "oppor",
        "url": "/service/oppor/456"
      },
      "AccountId": "123",
      "Id": "456",
      "ProposalID": "103"
    }
  ]
}

在上面的 JSON 中,字段“totalSize”、“done”、“records”和“attributes”是已知字段。而“AccountId”、“Id”和“ProposalID”是未知字段。在上面的 JSON 中,我不需要“属性”成为我的 bean 对象的一部分。

这是我的 JSON 的等效 bean 类

public class Result {
    private int totalSize;
    private boolean done;
    private List<Map<String, String>> records;

    public int getTotalSize() {
        return totalSize;
    }

    public void setTotalSize(int totalSize) {
        this.totalSize = totalSize;
    }

    public boolean isDone() {
        return done;
    }

    public void setDone(boolean done) {
        this.done = done;
    }

    public List<Map<String,String>> getRecords() {
        return records;
    }

    public void setRecords(List<Map<String, String>> records) {
        this.records = records;
    }

}

因此,我刚刚使用 List 获取 bean 中的 results 元素的 records 元素中有未知字段。在这张地图中,我不想要“属性”字段。解析时如何忽略这一点? 下面是我得到的例外,因为属性不是字符串元素。

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
 at [Source: [B@66fdec9; line: 1, column: 40] (through reference chain: com.sample.json.Result["records"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:691)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:46)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringMap(MapDeserializer.java:430)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:312)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:26)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:227)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:204)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:23)

【问题讨论】:

  • stackoverflow.com/a/7272468/492884。类似的东西应该可以工作。
  • @i_v_harish - 引用的答案是将 java 对象转换为 JSON。在这种情况下,我可以使用 @JsonProperty 注释。但对我来说,我需要从 JSON 获取 java 对象,并且我想忽略 JSON 字段

标签: java json jackson


【解决方案1】:

如果您有不想映射的特定字段,您可以在字段名称上使用 @JsonIgnore 注释

public class MyJsonObj{
@JsonProperty("name")
String fieldName    

@JsonIgnore
String fieldNameIgnored;
}

如果您想忽略 POJO 中未提及的所有字段,可以使用 @JsonIgnoreProperties 类上的注释

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyJsonObj{
strong text
}

【讨论】:

    【解决方案2】:

    2015 年 8 月 29 日更新:

    正如你所评论的那样

    我通过将 JSON 解析为地图实现了动态字段支持。忽略错误的 JSON 元素是待处理的事情

    我建议您应该处理原始 JSONObject 以从中删除 "attributes" 元素。

    原始JSONObject,例如:

    {
      "totalSize": 4,
      "done": true,
      "records": [
        {
          "attributes": {
            "type": "oppor",
            "url": "/service/oppor/456"
          },
          "AccountId": "123",
          "Id": "456",
          "ProposalID": "103"
        }
      ]
    }
    

    处理后,新的 JSONObject 将如下所示:

    {
        "records": {
            "AccountId": "123",
            "Id": "456",
            "ProposalID": "103"
        },
        "totalSize": 4,
        "done": true
    }
    

    使用如下代码:

            JSONObject jsonObject;
            try {            
                jsonObject = new JSONObject(jsonString1);
                JSONArray jsonArray = new JSONArray(jsonObject.get("records").toString());            
                JSONObject jsonObject1 = jsonArray.getJSONObject(0);
                jsonObject1.remove("attributes");
                jsonObject.put("records", jsonObject1);
            } catch (JSONException e) {
                e.printStackTrace();
            }
    

    然后,使用您自己的代码achieved dynamic field support by parsing the JSON into map

    2015/08/29 更新结束

    我建议你在这种情况下使用Gsontransient

    这样

            String jsonString1 = "{\n" +
                    "  \"totalSize\": 4,\n" +
                    "  \"done\": true,\n" +
                    "  \"records\": [\n" +
                    "    {\n" +
                    "      \"attributes\": {\n" +
                    "        \"type\": \"oppor\",\n" +
                    "        \"url\": \"/service/oppor/456\"\n" +
                    "      },\n" +
                    "      \"AccountId\": \"123\",\n" +
                    "      \"Id\": \"456\",\n" +
                    "      \"ProposalID\": \"103\"\n" +
                    "    }\n" +
                    "  ]\n" +
                    "}";
    
            Gson gson = new Gson();
            Result result1 = gson.fromJson(jsonString1, Result.class);
    

    你的课,关注transient

    public class Result {
        private int totalSize;
        private boolean done;
        private List<Record> records;
    }
    
    public class Record {
        private transient Map<String, String> attributes;
        private int AccountId;
        private int Id;
        private int ProposalID;
    }
    

    你会得到结果:

    P/S:我在 Android Studio 中测试过 :)

    更新:

          String jsonString1 = "{\n" +
                    "  \"totalSize\": 4,\n" +
                    "  \"done\": true,\n" +
                    "  \"records\": [\n" +
                    "    {\n" +
                    "      \"attributes\": {\n" +
                    "        \"type\": \"oppor\",\n" +
                    "        \"url\": \"/service/oppor/456\"\n" +
                    "      },\n" +
                    "      \"AccountId\": \"123\",\n" +
                    "      \"Id\": \"456\",\n" +
                    "      \"ProposalID\": \"103\"\n" +
                    "    }\n" +
                    "  ]\n" +
                    "}";
            Gson gson = new Gson();
            Object object = gson.fromJson(jsonString1, Object.class);
            Map<String, String> stringMap = (Map<String, String>) object;
            Result myResult = new Result();
            Iterator entries = stringMap.entrySet().iterator();
            while (entries.hasNext()) {
                Map.Entry entry = (Map.Entry) entries.next();
                String key = entry.getKey().toString();
                String value = entry.getValue().toString();
                switch (key) {
                    case "totalSize":
                        myResult.totalSize = (int) Double.parseDouble(entry.getValue().toString());
                        break;
                    case "done":
                        myResult.done = Boolean.valueOf(entry.getValue().toString());
                        break;
                    case "records":
                        try{
                            Object object1 = entry.getValue();
                            List<Object> objectList = (List<Object>) object1;
                            Map<String, Object> stringMap2 = (Map<String, Object>) objectList.get(0);
                            Map<String, String> recordMap = new HashMap<>();
                            Iterator entries2 = stringMap2.entrySet().iterator();
                            while (entries2.hasNext()) {
                                Map.Entry entry2 = (Map.Entry) entries2.next();
                                String key2 = entry2.getKey().toString();
                                String value2 = entry2.getValue().toString();
                                if (!"attributes".equals(key2)) {
                                    recordMap.put(key2, value2);
                                }
                                entries2.remove();
                            }
                            myResult.records = recordMap;
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        break;
                }
                entries.remove();
            }
    

    类:

    public class Result {
        private int totalSize;
        private boolean done;
        private Map<String, String> records;        
    }
    

    调试结果:

    【讨论】:

    • accountID、id、approvalID都是未知字段。从某种意义上说,未知字段可能出现在 json 中,也可能不出现。或者可能还有一些其他字段,如 opporId、proposalId 等。所以我不能将这些元素声明为我的 Record 类中的字段。这就是我将元素读取为地图的原因
    • 例如,如果 AccountId 没有出现在 json 字符串中,那么它将获得默认值 0。我的代码仍然有效。对于其他字段,IMO,您应该使用 Object object = gson.fromJson(jsonString1, Object.class); 然后解析结果对象以获得所需的值
    • 这个 JSON 对我也有效{ "totalSize": 4, "done": true, "records": [ { "attributes": { "type": "oppor", "url": "/service/oppor/456" }, "opporId": "123", "Id_1": "456", "Proposal_ID": "103" } ] } 我不想将它作为对象读取,而是将其读取为 Map。我只想忽略attributes 字段
    • 我不明白你的新评论是什么意思:)
    • 您可以在上面的评论中看到 json 的差异。没有 AccountId。而且我不希望这个字段本身出现在 Result 类中。即不具有默认值。我的要求是使用此 JSON 构造 [dataframe] (spark.apache.org/docs/latest/sql-programming-guide.html)。将数据框视为表格。因此,如果 JSON 中不存在某个元素,我只是不想创建具有默认值的数据框。
    【解决方案3】:

    我建议使用[Google gson API][1]'s @Expose annotation.(如果您的环境允许的话)。

    您可以简单地对生成的 json 文件中所需的字段(使用@Expose)进行注释,并将其保留为其他字段。并且在生成json的过程中,使用API​​方法,excludeFieldsWithoutExposeAnnotation

    样例可见here

    注意:在您的示例中,将您的Result 视为主要 POJO,records 是另一个具有 attributesaccountId 等字段的 POJO。 那么他们之间就有了has-a关系(Java composition)。

    然后,您可以调用 Json 到 pojo 转换,如下所示--

    com.google.gson.Gson gson = new com.google.gson.GsonBuilder()
                        .excludeFieldsWithoutExposeAnnotation().create();
    
     Result result= gson.fromJson(yourjsonString, Result.class);
    

    【讨论】:

    • 字段 accountId, Id... 是动态字段。它可能存在于 JSON 中,也可能不存在于 JSON 中。结果可能有不同的字段,如 opprId、prop_id 等。只有属性字段是静态字段,我想忽略它。在那种情况下,我需要如何定义我的 Result 类?
    【解决方案4】:

    1) 创建一个 Record 类对象

    2) 在你不会的字段上添加@JsonIgnore Annotation

    public class Result {
        private int totalSize;
        private boolean done;
        private Record records;
    
    [..]
    
    }
    
    
    public class Record {
        @JsonIgnore
        private Map<String, String> attributes;
        private int accountID;
        private int id;
        private int approvalID;
    
    [..]
    
    }
    

    【讨论】:

    • accountID、id、approvalID都是未知字段。从某种意义上说,未知字段可能出现在 json 中,也可能不出现。或者可能还有一些其他字段,如 opporId、proposalId 等。所以我不能将这些元素声明为我的 Record 类中的字段。这就是我将元素读取为地图的原因
    【解决方案5】:

    为属性创建一个新的 POJO 类,

    public class Result {
        private int totalSize;
        private boolean done;
        private List<Attributes> records;
    
        // Your Getters & Setters
    }
    
    public class Attributes{
        List<Map<String,String>> attributes;
        // Add other variables if necessary like AccountId, etc.,
    
        // Your Getters & Setters
    }
    

    【讨论】:

    • 我不需要“属性”成为我的 bean 对象的一部分
    • @SamuelAlexander 你的意思是你不需要属性变量或属性类?
    • 我不需要将“属性”字段读取为 POJO。基本上从上面的 JSON 我需要“totalSize”、“done”和“records”。在“记录”中,我需要 JSON 中存在的所有元素,除了“属性”
    • @SamuelAlexander 然后尝试使用 private Object records; 而不是 private List records; 并尝试通过以下方式从该对象中提取值相应地铸造。我不确定这是否正常工作或铸造是否会有任何问题。这只是一个建议。
    • 非常感谢您的建议。事实上我的电流是这样的。我在阅读地图中的字段时使用对象并忽略“属性”。但是如果我们在解析自己的时候忽略“属性”,代码会好很多
    猜你喜欢
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-16
    • 2019-01-16
    相关资源
    最近更新 更多