在Java中将JSON转换为外观不同的CSV
我需要写一个代码,将JSON文件转换为CSV。问题出在CSV文件的格式上 输入json:在Java中将JSON转换为外观不同的CSV,java,json,csv,Java,Json,Csv,我需要写一个代码,将JSON文件转换为CSV。问题出在CSV文件的格式上 输入json: { "strings":{ "1level1":{ "1level2":{ "1label1":"1value1", "1label2":"1value2" }
{
"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();
}
}
publicstaticvoid jsonToCsv()抛出JSONException{
InputStream=MainClass.class.getResourceAsStream(“/fromJson.json”);
JSONTokener JSONTokener=新的JSONTokener(is);
JSONObject JSONObject=新的JSONObject(jsonTokener);
stepino(jsonObject);
}
私有静态void单步插入(JSONObject JSONObject){
JSONObject对象=JSONObject;
试一试{
SetkeySet=object.keySet();
用于(字符串键:键集){
object=object.getJSONObject(键);
进入(客体);
}
}捕获(JSONException e){
SetkeySet=object.keySet();
用于(字符串键:键集){
System.out.println(object.get(key));
}
e、 printStackTrace();
}
}
更新#2:
另一个问题是,我永远不会知道JSON对象的名称和子对象的数量(同时更新JSON和CSV示例以使图像更清晰)。众所周知,它总是以字符串对象开头
使用的图书馆:
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
org.json
json
20180813
我想你只是忘记了记录你的结果,否则看起来不错
假设您的结果是一个简单的字符串。然后,在遍历json对象时必须连接所有键,直到达到一个基本值(如数字或字符串)
(这是我自己写的,请原谅我的语法错误)
如你所见,我没有改变你原来的方法。唯一的区别是,现在我们跟踪每个递归调用的键(“level1.level2…”
)
编辑
我添加了一个+“\n”所以每次我们到达一个值,我们就可以终止那条“线”
和更重要的是,我不是每次都返回,而是将每次调用的结果添加到字符串中,因此我们继续循环键并连接所有结果。该方法的每次调用只会在所有键循环一次后返回。(很抱歉错过了)
在调用方法中,您可以打印出结果,类似于:
JSONObject jsonObject = new JSONObject(jsonTokener);
String result = stepInto(jsonObject);
System.out.println(result);
因此,我自己找到了一个解决方案:
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;
}
}
你为什么不发布你的递归解决方案,然后我们就可以帮你找到问题了?我很确定你不需要任何花哨的json库。任何有效的JSON都只是一个带有键值对的map
。您应该能够逐级遍历该地图并构建CSV线。@Gamedroid我添加了我尝试使用的代码(我知道这不好),并添加了一些注释以使图像更清晰。请再看一次,问题是,在这种情况下,它将只通过第一个树(“1Level 1.1Level 2…”),而忽略另一个树(“2Level 1.2Level 2…”),不是吗?请查看我的编辑。我忘记了该方法将在第一个值处终止。现在它继续循环并应该遍历此代码的整个对象输出:strings.2level1.2level2.2label2.2value2
2level3.2label1.2value1
2level3.{“2label1”:“2value1”}
2level2.{“2label2”:“2value2”,“2level3”:{“2label1”:“2value1”}
2level1.{“2label2”:“2label2”:“2label2”,“2level3”:{“2label1”:“2value1”}}
1level1.1level2.1label2.1value2
1label1.1value1
1level2.{“1label2”:“1value2”、“1label1”:“1value1”}
1level1.{“1level2”:{“1label2”:“1label2”:“1value1”}
字符串。{“2level1”:“2label2”:“2label2”:“1value1”{1level2:{“1label2”:“1value2”,“1label1”:“1value1”}}}}
在main方法中创建StringBuilder并将其作为参数传递给stepInto有助于避免此类输出,但仍然没有找到让代码通过完整树的方法(每一行都以字符串开头。
以后可以很容易地替换。目前,我使用StringBuilder的建议获得了这样的输出:字符串。2level1.2level2.2label2,2value2
2level3.2label1,2value1
1level1.1label2.1value2
1label1,1value1
已经找到了解决方案和位置。)ted它。你可以检查一下你是否感兴趣。谢谢你!很好的解决方案!如果可以的话:你正在静态环境中运行你的方法,这很好。但是由于你的StringBuilder
是全局和静态的,你的程序只有一个生成器的实例-所以当你多次执行函数时最近,它将尝试(可能)用一个StringBuilder将不同的JSON读入不同的CSV。想法:将本地StringBuilder传递给您的stepInto
方法。您的jsonToCsv
方法保存并创建SB实例,并将其传递给stepInto
(您只需将其与递归调用一起传递)。你觉得怎么样?@GameDroids这段代码只是在单独的项目中编写的,以便于开发。当我将它转移到主项目时,我会按照你说的那样做:)但是谢谢你的建议!
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;
}
}