如何解析包含Java数组的Json

问题描述

我有一个Json,其中包含Arrays的数组,现在我需要解析Json并计算元素,达到一定限制后,我需要将其放入结果Json。我能够解析到一个级别并计算元素。如何解析多个级别并以相同格式获取对象:

这是我尝试解析一个不包含元素数量的级别的示例代码:

private void handleJson(Object jsonObj,CountObject c,JSONObject jsonObj) {
        Map<String,Object> map= new HashMap<>();

        if (jsonObj instanceof JSONObject) {

            parseJson(inputJSON,c,map,jsonObj);

        } 

        }

    }


private void parseJson(Object inputObj,Map<String,Object> map,JSONObject jsonObj) {
        JSONObject nodeJson = (JSONObject) inputJSON;
        Iterator<String> keyIter = nodeJson.keySet().iterator();

        while (keyIter.hasNext()) {
            String key = keyIter.next();
            Object value = nodeJson.get(key);

            if (value instanceof JSONObject) {
                int offSet = c.getOffSet();
                if(c.getLimit() == c.getOffSet()) {
                    break;
                }
            keyIter.remove(); 
            map.put(key,value); 
            c.setOffSet(++offSet);;
            } else {
                handleJSONArray(value,k,key);
            }
        }
        for (Entry<String,Object> entry : map.entrySet()) {
            jsonObj.put(entry.getKey(),entry.getValue());
}
    }


private void handleJSONArray(Object inputJSON,String key) {
        JSONArray nodeJsonArr = (JSONArray) inputJSON;
        int offSet = c.getOffSet();
        List<Object> ll = new ArrayList<>();
        for (int i = 0; i < nodeJsonArr.length(); i++) {
            Object value = nodeJsonArr.get(i);
            if (value instanceof JSONArray) {
                handleJSONArray(value,key2);
            } else {
                
                if (k.getLimit() == k.getOffSet()) {
                    break;
                }
                ll.add(value);
                ++offSet;
            }
        }
        map.put(key2,ll);
        c.setOffSet(offSet);
    }

这是我的Json:

{
"emails": [
{
"emails": [
{
"email": {
"id": "ac9e95cf-3338-4094-b465-e0e1deca23c4","value": "hello@gmail.com"
}
}
]
},{
"email": {
"id": "b61ffb48-ffc7-4ae6-81a2-78b632892fda","value": "hello1@gmail.com"
}
}
],"lastName": {
"id": "ffe19ece-819b-4680-8e0b-8566b34c973d","value": "FirstName"
},"firstName": {
"id": "4ed234f4-f679-40f3-b76b-41d9fdef7390","value": "LastName"
}
}

count对象是一个具有偏移量和Limit变量的Pojo,如果我将limit传递为3,那么我应该只获取前3个具有相同json格式的元素,如下所示:

{
"emails": [
{
"emails": [
{
"email": {
"id": "ac9e95cf-3338-4094-b465-e0e1deca23c4","value": "FirstName"
}

在这里我给出了一个示例JSON文件,并且Json可以包含任何数量的内部元素数组,逻辑应该能够解析任何类型的Json。 在这里,我还应该对Json元素进行分页,这意味着如果我通过offSet和limit,则应该相应地获取元素。在上面的示例中,CountObject包含limit和offSet,它基于该元素应获取元素。 要给出更多的解释,如果我将offSet设置为10并限制为10,则应该从第10个元素到第20个元素中提取元素,依此类推。

解决方法

这是使用Jackson的一种方法(我使用的是2.11.1版)。

此处的“ 项目”被定义为源JSON中的ID /值对之一,例如:

{
  "id": "b61ffb48-ffc7-4ae6-81a2-78b632892fda","value": "hello1@gmail.com"
}

我将任务分为两部分:

  1. 通过删除后续项,在达到所需限制时切断数据。

  2. 清理所有生成的空对象或数组。

这是我的输入测试数据(基于问题中提供的数据):

    private static final String JSON = "{\n"
            + " \"emails\": [{\n"
            + "         \"emails\": [{\n"
            + "             \"email\": {\n"
            + "                 \"id\": \"ac9e95cf-3338-4094-b465-e0e1deca23c4\",\n"
            + "                 \"value\": \"hello@gmail.com\"\n"
            + "             }\n"
            + "         }]\n"
            + "     },\n"
            + "     {\n"
            + "         \"email\": {\n"
            + "             \"id\": \"b61ffb48-ffc7-4ae6-81a2-78b632892fda\",\n"
            + "             \"value\": \"hello1@gmail.com\"\n"
            + "         }\n"
            + "     }\n"
            + " ],\n"
            + " \"lastName\": {\n"
            + "     \"id\": \"ffe19ece-819b-4680-8e0b-8566b34c973d\",\n"
            + "     \"value\": \"LastName\"\n"
            + " },\n"
            + " \"firstName\": {\n"
            + "     \"id\": \"4ed234f4-f679-40f3-b76b-41d9fdef7390\",\n"
            + "     \"value\": \"FirstName\"\n"
            + " }\n"
            + "}";

代码:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.Iterator;

public class JsonReducer {

    // get the first n "id/value" items:
    private final int limit = 2;
    // tracks how close we are to the cutoff limit:
    private int counter = 0;

    public void doParsing() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        JsonNode json = mapper.readValue(JSON,JsonNode.class);
        
        // show the input JSON formatted,just for this demo:
        System.out.println(json.toPrettyString());

        // a copy of the original - we will use this when cleaning up:
        JsonNode prevJson = json.deepCopy();
        // remove unwanted items from the JSON
        json = reduce(json);

        // clean up empty nodes resulting from removals:
        while (!json.equals(prevJson)) {
            prevJson = json.deepCopy();
            json = stripEmpty(json);
        }

        System.out.println("---------------------------------");
        System.out.println(json.toPrettyString());
    }

    private JsonNode reduce(JsonNode json) {
        for (JsonNode node : json) {
            if (node.isObject()) {
                counter++;
                //System.out.println("obj " + counter + " - " + node.toString());
                if (counter > limit) {
                    ((ObjectNode) node).removeAll();
                } else {
                    reduce(node);
                }
            } else if (node.isArray()) {
                ArrayNode arrayNode = (ArrayNode) node;
                //System.out.println("array - " + arrayNode.toString());
                arrayNode.forEach((item) -> {
                    // assume each item is a JSON object - no arrays of arrays:
                    ObjectNode objectNode = (ObjectNode) item;
                    reduce(objectNode);
                });
            } //else if (node.isTextual()) {
            //System.out.println("text  - " + node.asText());
            //}
        }
        return json;
    }

    private JsonNode stripEmpty(JsonNode json) {
        Iterator<JsonNode> it = json.iterator();
        while (it.hasNext()) {
            JsonNode child = it.next();
            if (child.isContainerNode() && child.isEmpty()) {
                it.remove(); // remove empty arrays [],and objects {}
            } else {
                stripEmpty(child);
            }
        }
        return json;
    }

    private static final String JSON = ... // as shown above.

}

reduce()方法递归地遍历JSON,跟踪收集到的项目数量-然后删除超出所需数量的项目。

但是,这可能会在JSON中留下空的[]数组或{}对象,因此stripEmpty()方法会对此进行处理。

因为我们要从上到下,从外到内依次遍历JSON,所以可能需要多次stripEmpty()方法。可能有一种更有效的方法,只需要通过一次,但这至少是简单的方法。

结果示例:

对于限制= 2:

{
  "emails" : [ {
    "emails" : [ {
      "email" : {
        "id" : "ac9e95cf-3338-4094-b465-e0e1deca23c4","value" : "hello@gmail.com"
      }
    } ]
  },{
    "email" : {
      "id" : "b61ffb48-ffc7-4ae6-81a2-78b632892fda","value" : "hello1@gmail.com"
    }
  } ]
}

对于限制= 1:

{
  "emails" : [ {
    "emails" : [ {
      "email" : {
        "id" : "ac9e95cf-3338-4094-b465-e0e1deca23c4","value" : "hello@gmail.com"
      }
    } ]
  } ]
}

限制= 0:

{ }

附加点:

非通用

该方法假定在其他数组内直接嵌套任何数组-因此都没有:[ [ {...} ] ]。换句话说,这不是100%的通用解析器,但确实与问题中的样本数据相符。

考虑使用POJO

此解决方案未定义将数据加载到其中的任何POJO Java对象-但这样做通常可以更轻松地获得所需的内容:

  • 将数据加载(反序列化)到一个或多个POJO中。
  • 从POJO中删除不需要的数据。
  • 将剩余数据序列化回JSON。

如果该示例比问题中的示例更复杂,我想我愿意这样做,而不是仅操作JsonNode数据。

更新

鉴于问题的变化,我认为我建议的最佳方法是将每个“ 项目”(请参见上面的定义)解析为一个POJO,该POJO仅包含3个字段:

String attribute;
String id;
String value;

执行此操作的代码如下:

    private void traverse(JsonNode json) {
        Iterator<Map.Entry<String,JsonNode>> it = json.fields();
        while (it.hasNext()) {
            Map.Entry<String,JsonNode> entry = it.next();
            String name = entry.getKey();
            JsonNode node = entry.getValue();

            if (node.isArray()) {
                ArrayNode arrayNode = (ArrayNode) node;
                arrayNode.forEach((item) -> {
                    // assume each item is a JSON object - no arrays of arrays:
                    ObjectNode objectNode = (ObjectNode) item;
                    traverse(objectNode);
                });
            } else {
                String id = node.get("id").asText();
                String value = node.get("value").asText();
                
                System.out.println("attr : " + name);
                System.out.println("id   : " + id);
                System.out.println("value: " + value);
                System.out.println("---");
            }
        }
    }

您可以创建一个新的POJO实例,而不是println()语句,并将其添加到ArrayList中。

现在您有了一个包含所有数据的标准列表-您可以根据用户界面的需要访问项目1-100,然后101- 200 ...,依此类推。

您当然需要将原始POJO数据转换回UI需要/期望的任何格式。

使用问题中的示例JSON,上述方法将显示以下内容:

attr : email
id   : ac9e95cf-3338-4094-b465-e0e1deca23c4
value: hello@gmail.com
---
attr : email
id   : b61ffb48-ffc7-4ae6-81a2-78b632892fda
value: hello1@gmail.com
---
attr : lastName
id   : ffe19ece-819b-4680-8e0b-8566b34c973d
value: LastName
---
attr : firstName
id   : 4ed234f4-f679-40f3-b76b-41d9fdef7390
value: FirstName

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...