将JSON转换为不同外观的Java CSV

huangapple go评论144阅读模式
英文:

Convert JSON to different looking CSV in Java

问题

以下是翻译好的部分:


我需要编写一个代码,将JSON文件转换为CSV。问题在于CSV文件的格式应如下所示。

输入的JSON:

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

对于这个JSON,期望的CSV文件如下:

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

我尝试使用递归遍历JSON文件,但由于在每次迭代中重写JSON对象,这对我来说是不起作用的,而且代码只能在第一个值之前运行。关于如何实现此目标,是否有任何建议?

注:我尝试使用不同的JSON库,因此目前可以使用其中任何一个。

更新#1:以下是我尝试使用的无效代码示例,用于遍历JSON树:

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();
    }
}

更新#2:另一个问题是我永远不会知道JSON对象的名称和子对象的数量(还要更新JSON和CSV示例,以使问题更加清晰)。唯一已知的是它总是从 strings 对象开始。

使用的库:

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

英文:

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:

{
   &quot;strings&quot;:{
      &quot;1level1&quot;:{
         &quot;1level2&quot;:{
            &quot;1label1&quot;:&quot;1value1&quot;,
            &quot;1label2&quot;:&quot;1value2&quot;
         }
      },
      &quot;2level1&quot;:{
         &quot;2level2&quot;:{
            &quot;2level3&quot;:{
               &quot;2label1&quot;:&quot;2value1&quot;
            },
            &quot;2label2&quot;:&quot;2value2&quot;
         }
      }
   }
}

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(&quot;/fromJson.json&quot;);
    JSONTokener jsonTokener = new JSONTokener(is);

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

private static void stepInto(JSONObject jsonObject) {
    JSONObject object = jsonObject;
    try {
        Set &lt; String &gt; keySet = object.keySet();
        for (String key: keySet) {
            object = object.getJSONObject(key);
            stepInto(object);
        }
    } catch (JSONException e) {
        Set &lt; String &gt; 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:

&lt;dependency&gt;
    &lt;groupId&gt;org.json&lt;/groupId&gt;
    &lt;artifactId&gt;json&lt;/artifactId&gt;
    &lt;version&gt;20180813&lt;/version&gt;
&lt;/dependency&gt;

答案1

得分: 1

自己找到了解决方案:

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;
    }
}
英文:

So found a solution by myself:

public static void jsonToCsv() throws JSONException, IOException {
InputStream is = MainClass.class.getResourceAsStream(&quot;/fromJson.json&quot;);
JSONTokener jsonTokener = new JSONTokener(is);
JSONObject jsonObject = new JSONObject(jsonTokener).getJSONObject(&quot;strings&quot;);
builder = new StringBuilder();
while (!jsonObject.isEmpty()) {
stepInto(jsonObject);
}
String[] lines = builder.toString().split(&quot;\n&quot;); // builder lines are in reverse order from expected so this array is used to reverse them
FileWriter csvWriter = new FileWriter(&quot;src/main/resources/toCsv.csv&quot;);
csvWriter.append(&quot;Keys,Default (en_US)\n&quot;);
for (int i = lines.length - 1; i &gt;= 0; i--) {
csvWriter.append(lines[i]).append(&quot;\n&quot;);
}
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(&quot;.&quot;);
stepInto(jsonObject.getJSONObject(key));
} else {
builder.append(key).append(&quot;,&quot;).append(object).append(&quot;\n&quot;);
jsonObject.remove(key);
break;
}
if (jsonObject.getJSONObject(key).isEmpty()) {
jsonObject.remove(key);
}
break;
}
}

答案2

得分: 0

我认为你只是错过了跟踪结果的步骤,否则看起来是正确的。

假设你的结果是一个简单的字符串。然后,在遍历 JSON 对象时,您必须将所有键连接起来,直到达到原始值(如数字或字符串)为止。

(以下是我凭记忆写的,如果语法不正确,请原谅我)

private static String stepInto(JSONObject jsonObject) {  
    String result = "";
    try {
        for (String key: jsonObject.keySet()) {         
            Object object = jsonObject.get(key);        
            if(object instanceof JSONObject){
               result+= key+"."+stepInto(jsonObject.getJSONObject(key));  
            }
            if(object instanceof JSONArray){
               result+= key+", ";                     
            }
            result+= key +", "+ object +"\n";                    
        }
    } catch (JSONException e) {        
        for (String key: jsonObject.keySet()) {
            System.out.println(object.get(key));
        }
        e.printStackTrace();
        result+= "\n";                                        
    } 
    return result;  
}

正如你所看到的,我没有改变你原来的方法太多。唯一的区别是现在我们为每个递归调用跟踪键("level1.level2...")。

编辑

我添加了 +"\n";,这样每次我们达到一个值时,我们可以终止那一行。

以及 更重要的是,我不是每次都返回,而是将每次调用的结果添加到一个字符串中,这样我们可以继续循环遍历键并连接所有结果。方法的每次调用将仅在循环遍历所有键后才返回一次。(抱歉我之前漏掉了这一点)

在您的调用方法中,您可以打印出结果,类似于:

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

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 &quot;void&quot; to &quot;String&quot; so we can record the results of each recursive &quot;stepInto&quot; call
//JSONObject object = jsonObject;                    // we don&#39;t need that. Both variables are the same object
String result =&quot;&quot;;
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+&quot;.&quot;+stepInto(jsonObject.getJSONObject(key));  // the recursive call, returning all keys concatenated to &quot;level1.level2.level3&quot; until we reach a primitive value
}
if(object instanceof JSONArray){
result+= key+&quot;, &quot;+ ...                      // 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 +&quot;, &quot;+ object +&quot;\n&quot;;                    // 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+= &quot;\n&quot;;                                        // 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 &quot;return&quot; 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 (&quot;level1.level2...&quot;) for each recursive call.

EDIT

I added a +&quot;\n&quot;; 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);

huangapple
  • 本文由 发表于 2020年8月17日 22:13:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63452677.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定