Flattening a 3 level nested JSON string in java

前端 未结 3 1097
眼角桃花
眼角桃花 2020-12-18 14:43

The requirement is to create a generic flattening utility for an input JSON object to a flattened JSON object.

The sample JSON looks like the below

         


        
相关标签:
3条回答
  • 2020-12-18 15:23

    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:

    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
    
    0 讨论(0)
  • 2020-12-18 15:33

    To avoid conflicts with key names you can use JSON Pointer specification to create them. It is also supported by Jackson library, so you can use them later to traverse the JsonNode node.

    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
    
    0 讨论(0)
  • 2020-12-18 15:40

    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:

    {
     "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. Lastly be sure that the level separator (here ::) will not be present in your key.

    HTH

    0 讨论(0)
提交回复
热议问题