简体   繁体   English

在 java 中展平 3 级嵌套 JSON 字符串

[英]Flattening a 3 level nested JSON string in java

The requirement is to create a generic flattening utility for an input JSON object to a flattened JSON object.要求是为输入 JSON object 创建一个通用展平实用程序到展平的 JSON ZA8Z6FDE6331BD4B666AC

The sample JSON looks like the below样品 JSON 如下所示

{
  "Source": "source-1",
  "Rows": [
    {
      "Keys": {
        "device-id": "BC04-EBH-N3K-01",
        "interface-name": "TenGigE0/0/0/39",
        "node-name": "0/0/CPU0"
      },
      "Timestamp": 1567621527656,
      "inner": {
        "donm": {
          "id": "0062",
          "mol": {
            "rem": 30,
            "len": 11,
            "org": {
              "ldp": [
                {
                  "t": 486,
                  "o": 322
                },
                {
                  "t": 487,
                  "o": 32,
                  "twss": 1,
                  "tlv": "00:01"
                }
              ]
            },
            "chlen": 14,
            "poe": 5,
            "combs": 10,
            "chaype": 4,
            "rek": 0,
            "rem-um": 67
          },
          "detail": {
            "enas": "B,R",
            "systes": "B,R",
            "timng": 91,
            "syn": "C",
            "met-type": 0,
            "neses": {
              "lldEDIT": [
                {
                  "ium": 830,
                  "m": 1,
                  "ass": {
                    "ape": "ipv4",
                    "ipvs": "94"
                  }
                }
              ]
            },
            "pess": "0008",
            "por]d": 0,
            "pon": "BCtive",
            "sysme": "BC1"
          },
          "reme": "Bu1",
          "hean": 0,
          "porl": "Et1"
        }
      }
    }

  ],
  "Tey": {
    "epath": "Cgetail",
    "sustr": "MX",
    "coime": 1567621527653,
    "msp": 1567621527653,
    "come": 1567621527660,
    "nor": "BC5",
    "cid": 14789654
  }
}

I have been trying to flatten it to 3 levels and came up with the below utility.我一直在尝试将其展平为 3 个级别,并提出了以下实用程序。 But, The things are getting complicated when I have to deal with Arrays and values of type String, long, Timestamp, etc. Also, I am unable to understand how the nested keys can be maintained for uniqueness.但是,当我必须处理 Arrays 和 String、long、Timestamp 等类型的值时,事情变得复杂起来。此外,我无法理解如何维护嵌套键的唯一性。

public static Map<String,Object> flattenJson(JsonNode input){
        Map<String,Object> finalMap = new HashMap<>();

        ObjectMapper datamapper = new ObjectMapper();
        Map<String,Object> topLevelJsonMap = datamapper.convertValue(input,Map.class);
        Set<String> topLevelKeys = topLevelJsonMap.keySet();

        for(String topLevelKey : topLevelKeys){

            System.out.println("Key :::: "+topLevelKey);

            Object topLevelData = topLevelJsonMap.get(topLevelKey);
            System.out.println("value :::: "+topLevelData.toString());

            if(topLevelData instanceof ArrayNode){
                ArrayNode arrayOfData = (ArrayNode) topLevelData;
                for(JsonNode dataNode : arrayOfData){
                    flattenJson(input);
                }
            } else if(topLevelData instanceof JsonNode){
                Map<String,Object> innerLevelJsonMap = datamapper.convertValue(topLevelData,Map.class);
                Set<String> innerLevelKeys = innerLevelJsonMap.keySet();
                for(String innerLevelKey : innerLevelKeys){
                    System.out.println("inner key :::: "+innerLevelKey);
                    flattenJson((JsonNode) innerLevelJsonMap.get(innerLevelKey));
                }
            }else {
               finalMap.put(topLevelKey,topLevelData);
            }
        }
        return finalMap;
    }

Any help is greatly appreciated.任何帮助是极大的赞赏。

Try this code:试试这个代码:

public static void flattenJson(JsonNode node, String parent, Map<String, ValueNode> map) {
    if (node instanceof ValueNode) {
        map.put(parent, (ValueNode)node);
    } else {
        String prefix = parent == null ? "" : parent + ".";
        if (node instanceof ArrayNode) {
            ArrayNode arrayNode = (ArrayNode)node;
            for(int i = 0; i < arrayNode.size(); i++) {
                flattenJson(arrayNode.get(i), prefix + i, map);
            }
        } else if (node instanceof ObjectNode) {
            ObjectNode objectNode = (ObjectNode) node;
            for (Iterator<Map.Entry<String, JsonNode>> it = objectNode.fields(); it.hasNext(); ) {
                Map.Entry<String, JsonNode> field = it.next();
                flattenJson(field.getValue(), prefix + field.getKey(), map);
            }
        } else {
            throw new RuntimeException("unknown json node");
        }
    }
}

public static Map<String, ValueNode> flattenJson(JsonNode input) {
    Map<String, ValueNode> map = new LinkedHashMap<>();
    flattenJson(input, null, map);
    return map;
}

Then you can call然后你可以打电话

    ObjectMapper om = new ObjectMapper();
    JsonNode jsonNode = om.readTree(json);
    Map<String, ValueNode> m = flattenJson(jsonNode);
    for (Map.Entry<String, ValueNode> kv : m.entrySet()) {
        System.out.println(kv.getKey() + "=" + kv.getValue().asText());
    }

Output: Output:

Source=source-1
Rows.0.Keys.device-id=BC04-EBH-N3K-01
Rows.0.Keys.interface-name=TenGigE0/0/0/39
Rows.0.Keys.node-name=0/0/CPU0
Rows.0.Timestamp=1567621527656
Rows.0.inner.donm.id=0062
Rows.0.inner.donm.mol.rem=30
Rows.0.inner.donm.mol.len=11
Rows.0.inner.donm.mol.org.ldp.0.t=486
Rows.0.inner.donm.mol.org.ldp.0.o=322
Rows.0.inner.donm.mol.org.ldp.1.t=487
Rows.0.inner.donm.mol.org.ldp.1.o=32
Rows.0.inner.donm.mol.org.ldp.1.twss=1
Rows.0.inner.donm.mol.org.ldp.1.tlv=00:01
Rows.0.inner.donm.mol.chlen=14
Rows.0.inner.donm.mol.poe=5
Rows.0.inner.donm.mol.combs=10
Rows.0.inner.donm.mol.chaype=4
Rows.0.inner.donm.mol.rek=0
Rows.0.inner.donm.mol.rem-um=67
Rows.0.inner.donm.detail.enas=B,R
Rows.0.inner.donm.detail.systes=B,R
Rows.0.inner.donm.detail.timng=91
Rows.0.inner.donm.detail.syn=C
Rows.0.inner.donm.detail.met-type=0
Rows.0.inner.donm.detail.neses.lldEDIT.0.ium=830
Rows.0.inner.donm.detail.neses.lldEDIT.0.m=1
Rows.0.inner.donm.detail.neses.lldEDIT.0.ass.ape=ipv4
Rows.0.inner.donm.detail.neses.lldEDIT.0.ass.ipvs=94
Rows.0.inner.donm.detail.pess=0008
Rows.0.inner.donm.detail.por]d=0
Rows.0.inner.donm.detail.pon=BCtive
Rows.0.inner.donm.detail.sysme=BC1
Rows.0.inner.donm.reme=Bu1
Rows.0.inner.donm.hean=0
Rows.0.inner.donm.porl=Et1
Tey.epath=Cgetail
Tey.sustr=MX
Tey.coime=1567621527653
Tey.msp=1567621527653
Tey.come=1567621527660
Tey.nor=BC5
Tey.cid=14789654

To avoid conflicts with key names you can use JSON Pointer specification to create them.为避免与键名冲突,您可以使用JSON 指针规范来创建它们。 It is also supported by Jackson library, so you can use them later to traverse the JsonNode node. Jackson库也支持它,因此您以后可以使用它们来遍历JsonNode节点。

Simple implementation could look like below:简单的实现可能如下所示:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

public class JsonApp {

  public static void main(String[] args) throws Exception {
    File jsonFile = new File("./test.json");

    ObjectMapper mapper = new ObjectMapper();
    JsonNode root = mapper.readTree(jsonFile);
    Map<String, JsonNode> map = new JsonFlattener(root).flatten();

    System.out.println("Use key-value pairs:");
    map.forEach(
        (k, v) -> {
          System.out.println(k + " => " + v);
        });

    System.out.println();
    System.out.println("Use pointers:");
    map.forEach(
        (k, v) -> {
          System.out.println(k + " => " + root.at(k));
        });
  }
}

class JsonFlattener {

  private final Map<String, JsonNode> json = new LinkedHashMap<>(64);
  private final JsonNode root;

  JsonFlattener(JsonNode node) {
    this.root = Objects.requireNonNull(node);
  }

  public Map<String, JsonNode> flatten() {
    process(root, "");
    return json;
  }

  private void process(JsonNode node, String prefix) {
    if (node.isObject()) {
      ObjectNode object = (ObjectNode) node;
      object
          .fields()
          .forEachRemaining(
              entry -> {
                  process(entry.getValue(), prefix + "/" + entry.getKey());
              });
    } else if (node.isArray()) {
      ArrayNode array = (ArrayNode) node;
      AtomicInteger counter = new AtomicInteger();
      array
          .elements()
          .forEachRemaining(
              item -> {
                process(item, prefix + "/" + counter.getAndIncrement());
              });
    } else {
      json.put(prefix, node);
    }
  }
}

Above code prints:上面的代码打印:

Use key-value pairs:
/Source => "source-1"
/Rows/0/Keys/device-id => "BC04-EBH-N3K-01"
/Rows/0/Keys/interface-name => "TenGigE0/0/0/39"
/Rows/0/Keys/node-name => "0/0/CPU0"
/Rows/0/Timestamp => 1567621527656
/Rows/0/inner/donm/id => "0062"
/Rows/0/inner/donm/mol/rem => 30
/Rows/0/inner/donm/mol/len => 11
/Rows/0/inner/donm/mol/org/ldp/0/t => 486
/Rows/0/inner/donm/mol/org/ldp/0/o => 322
/Rows/0/inner/donm/mol/org/ldp/1/t => 487
/Rows/0/inner/donm/mol/org/ldp/1/o => 32
/Rows/0/inner/donm/mol/org/ldp/1/twss => 1
/Rows/0/inner/donm/mol/org/ldp/1/tlv => "00:01"
/Rows/0/inner/donm/mol/chlen => 14
/Rows/0/inner/donm/mol/poe => 5
/Rows/0/inner/donm/mol/combs => 10
/Rows/0/inner/donm/mol/chaype => 4
/Rows/0/inner/donm/mol/rek => 0
/Rows/0/inner/donm/mol/rem-um => 67
/Rows/0/inner/donm/detail/enas => "B,R"
/Rows/0/inner/donm/detail/systes => "B,R"
/Rows/0/inner/donm/detail/timng => 91
/Rows/0/inner/donm/detail/syn => "C"
/Rows/0/inner/donm/detail/met-type => 0
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ium => 830
/Rows/0/inner/donm/detail/neses/lldEDIT/0/m => 1
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ass/ape => "ipv4"
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ass/ipvs => "94"
/Rows/0/inner/donm/detail/pess => "0008"
/Rows/0/inner/donm/detail/por]d => 0
/Rows/0/inner/donm/detail/pon => "BCtive"
/Rows/0/inner/donm/detail/sysme => "BC1"
/Rows/0/inner/donm/reme => "Bu1"
/Rows/0/inner/donm/hean => 0
/Rows/0/inner/donm/porl => "Et1"
/Tey/epath => "Cgetail"
/Tey/sustr => "MX"
/Tey/coime => 1567621527653
/Tey/msp => 1567621527653
/Tey/come => 1567621527660
/Tey/nor => "BC5"
/Tey/cid => 14789654

Use pointers:
/Source => "source-1"
/Rows/0/Keys/device-id => "BC04-EBH-N3K-01"
/Rows/0/Keys/interface-name => "TenGigE0/0/0/39"
/Rows/0/Keys/node-name => "0/0/CPU0"
/Rows/0/Timestamp => 1567621527656
/Rows/0/inner/donm/id => "0062"
/Rows/0/inner/donm/mol/rem => 30
/Rows/0/inner/donm/mol/len => 11
/Rows/0/inner/donm/mol/org/ldp/0/t => 486
/Rows/0/inner/donm/mol/org/ldp/0/o => 322
/Rows/0/inner/donm/mol/org/ldp/1/t => 487
/Rows/0/inner/donm/mol/org/ldp/1/o => 32
/Rows/0/inner/donm/mol/org/ldp/1/twss => 1
/Rows/0/inner/donm/mol/org/ldp/1/tlv => "00:01"
/Rows/0/inner/donm/mol/chlen => 14
/Rows/0/inner/donm/mol/poe => 5
/Rows/0/inner/donm/mol/combs => 10
/Rows/0/inner/donm/mol/chaype => 4
/Rows/0/inner/donm/mol/rek => 0
/Rows/0/inner/donm/mol/rem-um => 67
/Rows/0/inner/donm/detail/enas => "B,R"
/Rows/0/inner/donm/detail/systes => "B,R"
/Rows/0/inner/donm/detail/timng => 91
/Rows/0/inner/donm/detail/syn => "C"
/Rows/0/inner/donm/detail/met-type => 0
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ium => 830
/Rows/0/inner/donm/detail/neses/lldEDIT/0/m => 1
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ass/ape => "ipv4"
/Rows/0/inner/donm/detail/neses/lldEDIT/0/ass/ipvs => "94"
/Rows/0/inner/donm/detail/pess => "0008"
/Rows/0/inner/donm/detail/por]d => 0
/Rows/0/inner/donm/detail/pon => "BCtive"
/Rows/0/inner/donm/detail/sysme => "BC1"
/Rows/0/inner/donm/reme => "Bu1"
/Rows/0/inner/donm/hean => 0
/Rows/0/inner/donm/porl => "Et1"
/Tey/epath => "Cgetail"
/Tey/sustr => "MX"
/Tey/coime => 1567621527653
/Tey/msp => 1567621527653
/Tey/come => 1567621527660
/Tey/nor => "BC5"
/Tey/cid => 14789654

Just create namespace of the keys by appending each level key into the key from higher level.只需通过将每个级别的键附加到更高级别的键中来创建键的命名空间。 In other words your flattened JSON's keys would be:换句话说,您扁平化的 JSON 键将是:

{
 "L1key::L2key": "L2val",
 "L1key": "L1val",
 "L1key::L2key::L3key": "L3val"
}

This way you guarantee uniqueness but also you can create the original json from this as well.这样您就可以保证唯一性,而且您也可以从中创建原始的 json。 Lastly be sure that the level separator (here :: ) will not be present in your key.最后确保您的密钥中不存在级别分隔符(此处为:: )。

HTH高温高压

You can take a look on json-flattener .你可以看看json-flattener

BTW, I am the author of this lib.顺便说一句,我是这个库的作者。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM