vodohleb
vodohleb

Reputation: 35

Convert JSON to different looking CSV in Java

I need to write a code which would convert JSON file to CSV. The problem is in a format that the CSV file should look like.

Input json:

{
   "strings":{
      "1level1":{
         "1level2":{
            "1label1":"1value1",
            "1label2":"1value2"
         }
      },
      "2level1":{
         "2level2":{
            "2level3":{
               "2label1":"2value1"
            },
            "2label2":"2value2"
         }
      }
   }
}

And this is expected csv file for this json:

Keys,Default
1level1.1level2.1label1,1value1
1level1.1level2.1label2,1value2
2level1.2level2.2level3.2label1,2value1
2level1.2level2.2label2,2value2

I was trying to go through JSON file using recursion but this didn't work for me because of rewriting JSON object on each iteration and code was working only till the first value. Are there any suggestions about how can it be done?

Note: have tried to use different JSON libraries, so for now can be used any of them

UPDATE #1: Non-working code example I was trying to use to go through JSON tree:

public static void jsonToCsv() throws JSONException {
    InputStream is = MainClass.class.getResourceAsStream("/fromJson.json");
    JSONTokener jsonTokener = new JSONTokener(is);

    JSONObject jsonObject = new JSONObject(jsonTokener);
    stepInto(jsonObject);
}

private static void stepInto(JSONObject jsonObject) {
    JSONObject object = jsonObject;
    try {
        Set < String > keySet = object.keySet();
        for (String key: keySet) {
            object = object.getJSONObject(key);
            stepInto(object);
        }
    } catch (JSONException e) {
        Set < String > keySet = object.keySet();
        for (String key: keySet) {
            System.out.println(object.get(key));
        }

        e.printStackTrace();
    }

}

UPDATE #2: Another issue is that I will never know the names of the JSON object and count of child objects (update JSON and CSV examples as well to make the image more clear). All that is known, that it will always start with strings object.

Library used:

<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20180813</version>
</dependency>

Upvotes: 0

Views: 1203

Answers (2)

vodohleb
vodohleb

Reputation: 35

So found a solution by myself:

public static void jsonToCsv() throws JSONException, IOException {
    InputStream is = MainClass.class.getResourceAsStream("/fromJson.json");
    JSONTokener jsonTokener = new JSONTokener(is);

    JSONObject jsonObject = new JSONObject(jsonTokener).getJSONObject("strings");
    builder = new StringBuilder();

    while (!jsonObject.isEmpty()) {
        stepInto(jsonObject);
    }

    String[] lines = builder.toString().split("\n"); // builder lines are in reverse order from expected so this array is used to reverse them

    FileWriter csvWriter = new FileWriter("src/main/resources/toCsv.csv");
    csvWriter.append("Keys,Default (en_US)\n");

    for (int i = lines.length - 1; i >= 0; i--) {
        csvWriter.append(lines[i]).append("\n");
    }

    csvWriter.flush();
    csvWriter.close();
}


private static void stepInto(JSONObject jsonObject) {
    for (String key: jsonObject.keySet()) {
        Object object = jsonObject.get(key);
        if (object instanceof JSONObject) {
            builder.append(key).append(".");
            stepInto(jsonObject.getJSONObject(key));
        } else {
            builder.append(key).append(",").append(object).append("\n");
            jsonObject.remove(key);
            break;
        }

        if (jsonObject.getJSONObject(key).isEmpty()) {
            jsonObject.remove(key);
        }
        break;
    }
}

Upvotes: 1

GameDroids
GameDroids

Reputation: 5662

I think you just missed keeping track of your result, otherwise it looks good.

Let's say your result is a simple string. Then you have to concatenate all keys while traversing the json object until you reach a primitive value (like a number or a string).

(I am writing this out of my head, so please forgive me for incorrect syntax)

private static String stepInto(JSONObject jsonObject) {  // we change "void" to "String" so we can record the results of each recursive "stepInto" call
    //JSONObject object = jsonObject;                    // we don't need that. Both variables are the same object
    String result ="";
    try {
        for (String key: jsonObject.keySet()) {          // shorter version
            Object object = jsonObject.get(key);         // Attention! we get a simple Java Object
            if(object instanceof JSONObject){
               result+= key+"."+stepInto(jsonObject.getJSONObject(key));  // the recursive call, returning all keys concatenated to "level1.level2.level3" until we reach a primitive value
            }
            if(object instanceof JSONArray){
               result+= key+", "+ ...                      // notice how we use the csv separator (comma) here, because we reached a value. For you to decide how you want to represent arrays
            }
            result+= key +", "+ object +"\n";                    // here I am not sure. It may well be that you need to check if object is a String an Integer, Float or anything.
        }
    } catch (JSONException e) {        
        for (String key: jsonObject.keySet()) {
            System.out.println(object.get(key));
        }
        e.printStackTrace();
        result+= "\n";                                        // I added this fallback so your program can terminate even when an error occurs.
    } 
    return result;  // sorry, I forgot to accumulate all results and return them. So now we have only one actual "return" statement that will terminate the call and return all results.

}

As you can see, I didn't change much of your original method. The only difference is that now we keep track of the keys ("level1.level2...") for each recursive call.

EDIT

I added a +"\n"; so everytime we reach a value so we can terminate that "line".

AND more importantly, instead of returning everytime, I add the result of each call to a string, so we continue looping over the keys and concatenate all results. Each call of the method will return only once all keys are looped over. (sorry that missed that)

In your calling method you could print out the result, something like that:

JSONObject jsonObject = new JSONObject(jsonTokener);
String result = stepInto(jsonObject);
System.out.println(result);

Upvotes: 0

Related Questions