0

I have an input json like:

{
    "Employee": [
      {
        "employee1.id": 1
      },
      {
        "employee1.name": "John"
      },
      {
        "employee1.address.street": "main street"
      },
      {
        "employee1.address.pin": "123456"
      },
      {
        "employee2.id": 2
      },
      {
        "employee2.name": "Mike"
      },
      {
        "employee2.address.street": "51 street"
      },
      {
        "employee2.address.pin": "234543"
      }
    ]
}

And I am trying to convert it into:

{
    "employee1":{
        "id": 1,
        "name": "John",
        "address": {
            "street": "main street",
            "pin": "123456"
        }
    },
        "employee2":{
        "id": 2,
        "name": "Mike",
        "address": {
            "street": "51 street",
            "pin": "234543"
        }
    }
}

I tried to split the key from input json with dot i.e. '.' and tried to iterate over to construct a map: Map<String, Object> But the problem is the input json key depth can go beyond 2 i.e. in future the input key can be like:

{
    "employee1.address.tempAddress.street": "main street"
},
{
    "employee1.address.permanentAddress.street": "main street"
}

So, is there any library available to achieve this, or anything closely related to this using which I can achieve this?

Arthur
  • 87
  • 6
  • 5
    You do realize that your input "json" isn't really JSON, right? Why not have a talk with your backend team about it? – Shark Jan 31 '23 at 15:47
  • What you are showing us is _almost_ valid JSON so maybe you are actually handling valid JSON. We can't be sure. Asking for library recommendations is [off-topic](https://stackoverflow.com/help/on-topic) for Stack Overflow, but you can take a look at [How to parse JSON in Java](https://stackoverflow.com/q/2591098/12567365) as a starting point. – andrewJames Jan 31 '23 at 15:50
  • @Shark, While copying the excerpt from the original Json the 'data' key got added. Edited the input json to be valid. – Arthur Jan 31 '23 at 15:52
  • @Arthur it's still not valid JSON though. It's a JSON Array, called "Employee" which has elements that represent different field of the imaginary parent employee. And then, the second employee starts in the middle of it. It would make more sense if it were a json array of json arrays - meaning, `Employees [ Employee: [ ], Employee: [ ] ... ]` The way it currently sits, there is no common POJO class to use for it, since it could be a mapping, or a mapping. Looks like you could use a SAX parser for this "json". – Shark Jan 31 '23 at 16:00
  • 1
    @Shark: the current input described here is absolutely valid JSON. It's not "nice" JSON or "well-designed" JSON, but it's JSON. – Joachim Sauer Jan 31 '23 at 16:36
  • @Shark, Unfortunately, I don't have the control over the incoming json data. – Arthur Jan 31 '23 at 16:36

2 Answers2

2

The comments about the input format being "weird" (to put it politely) are spot-on: This is a convoluted way to represent simple hierarchical data.

But sometimes we have to deal with sub-optimal input formats, that's why we can fix them like this:

  private static JSONObject arrayToStructure(JSONArray inputArray) {
    JSONObject output = new JSONObject();
    for (Object o : inputArray) {
      assert o instanceof JSONObject;
      JSONObject jso = (JSONObject) o;
      assert jso.length() == 1;
      String key = jso.keys().next();
      Object value = jso.get(key);
      setJsonPath(output, key, value);
    }
    return output;
  }

  private static void setJsonPath(JSONObject target, String key, Object value) {
    String[] keyParts = key.split("\\.");
    JSONObject parent = target;
    for (int i = 0; i < keyParts.length - 1; i++) {
      String keyPart = keyParts[i];
      if (parent.has(keyPart)) {
        parent = parent.getJSONObject(keyPart);
      } else {
        JSONObject child = new JSONObject();
        parent.put(keyPart, child);
        parent = child;
      }
    }
    assert !parent.has(keyParts[keyParts.length - 1]);
    parent.put(keyParts[keyParts.length - 1], value);
  }

This code uses JSON-Java, probably better known as org.json:json.

And it can be used like this:

  public static void main(String[] args) {
    String input = """
        [
          {
            "employee1.id": 1
          },
          {
            "employee1.name": "John"
          },
          {
            "employee1.address.street": "main street"
          },
          {
            "employee1.address.pin": "123456"
          },
          {
            "employee2.id": 2
          },
          {
            "employee2.name": "Mike"
          },
          {
            "employee2.address.street": "51 street"
          },
          {
            "employee2.address.pin": "234543"
          }
        ]""";
    JSONArray inputArray = new JSONArray(input);
    JSONObject structure = arrayToStructure(inputArray);
    System.out.println(structure.toString(2));
  }

Note that this code is lacking proper sanity checks (some of which I've hinted at using assert) and the nature of this "protocol" means that you can get misleading, ambiguous or straight up malicious inputs. For example nothing stops the input from having both "employee1.id": 1 and "employee1.id": 2 in there. How that is to be interpreted is up to the parser and such ambiguity is a great source of bugs and potential security issues.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • Should be fixed at the source, if the source is under control, if not - this seems like a great solution for the convoluted data source. – Shark Jan 31 '23 at 16:29
2

Library Josson can do the transformation by one expression.

https://github.com/octomix/josson

Josson josson = Josson.fromJsonString(
    "{" +
    "  \"Employee\": [" +
    "    {" +
    "      \"employee1.id\": 1" +
    "    }," +
    "    {" +
    "      \"employee1.name\": \"John\"" +
    "    }," +
    "    {" +
    "      \"employee1.address.street\": \"main street\"" +
    "    }," +
    "    {" +
    "      \"employee1.address.pin\": \"123456\"" +
    "    }," +
    "    {" +
    "      \"employee2.id\": 2" +
    "    }," +
    "    {" +
    "      \"employee2.name\": \"Mike\"" +
    "    }," +
    "    {" +
    "      \"employee2.address.street\": \"51 street\"" +
    "    }," +
    "    {" +
    "      \"employee2.address.pin\": \"234543\"" +
    "    }" +
    "  ]" +
    "}");
JsonNode node = josson.getNode("Employee.mergeObjects().unflatten('.')");
System.out.println(node.toPrettyString());

Output

{
  "employee1" : {
    "id" : 1,
    "name" : "John",
    "address" : {
      "street" : "main street",
      "pin" : "123456"
    }
  },
  "employee2" : {
    "id" : 2,
    "name" : "Mike",
    "address" : {
      "street" : "51 street",
      "pin" : "234543"
    }
  }
}
Raymond Choi
  • 1,065
  • 2
  • 7
  • 8