在JSON转换期间保持JSON键的顺序为CSV

时间:2010-12-23 03:40:14

标签: java json csv

我正在使用此处提供的JSON库http://www.json.org/java/index.html将我拥有的json字符串转换为CSV。 但我遇到的问题是,转换后键的顺序会丢失。

这是转换代码:

    JSONObject jo = new JSONObject(someString);
    JSONArray ja = jo.getJSONArray("items");
    String s = CDL.toString(ja);
    System.out.println(s);

这是“someString”的内容:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ]
}

结果如下:

WO,QU,WR,QA,NO
hasd,asd,qwe,end,qwer

虽然我期望保持按键的顺序:

WR,QU,QA,WO,NO
qwe,asd,end,hasd,qwer

有什么方法可以使用这个库得到这个结果?如果没有,是否还有其他库可以保持结果中键的顺序?

18 个答案:

答案 0 :(得分:91)

有(hacky)方法可以做到这一点......但你不应该这样做。

在JSON中,定义了一个对象:

  

对象是一组无序的名称/值对。

请参阅http://json.org

JSON的大多数实现都不会保留对象名称/值对的顺序,因为它(根据定义)并不重要。

如果要保留订单,则需要重新定义数据结构; e.g。

{
    "items":
    [
        [
            {"WR":"qwe"},
            {"QU":"asd"},
            {"QA":"end"},
            {"WO":"hasd"},
            {"NO":"qwer"}
        ],
    ]
}

或更简单:

{
    "items":
    [
        {"WR":"qwe"},
        {"QU":"asd"},
        {"QA":"end"},
        {"WO":"hasd"},
        {"NO":"qwer"}
    ]
}

<强>后续

  

感谢您的信息,但我别无选择,只能在我的应用程序中使用JSON,我的应用程序需要保持密钥的顺序,而不管JSON对象的定义...我不允许更改格式JSON文件也是......

您需要与设计该文件结构的任何人进行硬性对话,并且不允许您更改它。这是/他们是完全错误的。你需要来说服他们。

如果他们真的不允许你改变它:

  • 您应该坚持将其称为JSON ......“因为它不是。
  • 您应该指出,您将不得不专门编写/修改代码来处理这种“非JSON”格式...除非您能找到一些保留订单的JSON实现。如果他们是付费客户,请确保他们为您必须完成的额外工作付费。
  • 您应该指出,如果某个其他工具需要使用“非JSON”,则会出现问题。的确,这个问题会一遍又一遍地发生......

这种事情真的很糟糕。一方面,您的软件将违反旨在促进互操作性的完善/长期规范。另一方面,设计这种蹩脚(不是JSON!)文件格式的笨蛋可能会扼杀其他人的系统等因为系统无法应对他们的废话。

<强>更新

同样值得一读JSON RFC (RFC 7159)关于这个主题的内容。以下是一些摘录:

  

自RFC 4627发布以来,JSON已经找到了      广泛使用。这种经历揭示了某些模式,      虽然它的规范允许,但已经引起了互操作性      问题。

     

JavaScript Object Notation(JSON)是一种文本格式      结构化数据的序列化。 ...

     

JSON可以表示四种基本类型(字符串,数字,布尔值,      和null)和两个结构化类型(对象和数组)。

     

对象是零个或多个名称/值的无序集合      对,其中名称是字符串,值是字符串,数字,      boolean,null,object或array。

     

已经观察到JSON解析库在是否存在时会有所不同      不是它们使对象成员的排序对调用可见      软件。行为不依赖于成员的实现      排序将是可互操作的,因为它们不会      受到这些差异的影响。

答案 1 :(得分:45)

维持秩序非常简单。我在维护从DB层到UI层的顺序方面遇到了同样的问题。

打开JSONObject.java文件。它在内部使用HashMap,它不维护订单。

将其更改为LinkedHashMap:

    //this.map = new HashMap();
    this.map = new LinkedHashMap();

这对我有用。请在评论中告诉我。我建议JSON库本身应该有另一个维护顺序的JSONObject类,比如JSONOrderdObject.java。我很难选择名字。

答案 2 :(得分:21)

JSONObject.java会获取您传递的任何地图。它可能是LinkedHashMapTreeMap,只有在地图为空时才会显示hashmap

这是JSONObject.java类的构造函数,用于检查map。

 public JSONObject(Map paramMap)
  {
    this.map = (paramMap == null ? new HashMap() : paramMap);
  }

所以在构建json对象构造LinkedHashMap之前,然后将它传递给像这样的构造函数,

LinkedHashMap<String, String> jsonOrderedMap = new LinkedHashMap<String, String>();

jsonOrderedMap.put("1","red");
jsonOrderedMap.put("2","blue");
jsonOrderedMap.put("3","green");

JSONObject orderedJson = new JSONObject(jsonOrderedMap);

JSONArray jsonArray = new JSONArray(Arrays.asList(orderedJson));

System.out.println("Ordered JSON Fianl CSV :: "+CDL.toString(jsonArray));

因此无需更改JSONObject.java类。希望它对某人有帮助。

答案 3 :(得分:12)

对这类问题更详细但广泛适用的解决方案是使用一对数据结构:包含排序的列表和包含关系的映射。

例如:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ],
    "itemOrder":
        ["WR", "QU", "QA", "WO", "NO"]
}

您迭代itemOrder列表,并使用它们查找地图值。订购保留,没有任何损坏。

我多次使用过这种方法。

答案 4 :(得分:10)

使用反映的另一个hacky解决方案:

JSONObject json = new JSONObject();
Field map = json.getClass().getDeclaredField("map");
map.setAccessible(true);//because the field is private final...
map.set(json, new LinkedHashMap<>());
map.setAccessible(false);//return flag

答案 5 :(得分:6)

Apache Wink有OrderedJSONObject。它在解析String时保持顺序。

答案 6 :(得分:4)

偶然发现同样的问题,我相信作者使用的最终解决方案包括使用自定义的ContainerFactory:

public static Values parseJSONToMap(String msgData) {
    JSONParser parser = new JSONParser();
    ContainerFactory containerFactory = new ContainerFactory(){
        @Override
        public Map createObjectContainer() {
            return new LinkedHashMap();
        }

        @Override
        public List creatArrayContainer() {
            return null;
        }
    };
    try {
        return (Map<String,Object>)parser.parse(msgData, containerFactory);
    } catch (ParseException e) {
        log.warn("Exception parsing JSON string {}", msgData, e);
    }
    return null;
}  

http://juliusdavies.ca/json-simple-1.1.1-javadocs/org/json/simple/parser/JSONParser.html#parse(java.io.Reader,org.json.simple.parser.ContainerFactory)

答案 7 :(得分:3)

解决。

我使用了https://code.google.com/p/json-simple/中的JSON.simple库来读取JSON字符串以保持键的顺序,并使用此处http://sourceforge.net/projects/javacsv/的JavaCSV库转换为CSV格式。

答案 8 :(得分:2)

我知道这已经解决了很久以前问了这个问题,但是当我处理类似的问题时,我想对此采取完全不同的方法:

对于数组,它说“数组是有序的值集合”。在http://www.json.org/ - 但是对象(“一个对象是一组无序的名称/值对。”)不是有序的。

我想知道为什么那个对象在一个数组中 - 这意味着一个不在那里的命令。

{
"items":
[
    {
        "WR":"qwe",
        "QU":"asd",
        "QA":"end",
        "WO":"hasd",
        "NO":"qwer"
    },
]
}

因此,解决方案是将密钥放在“真正的”数组中,并将数据作为对象添加到每个密钥中,如下所示:

{
"items":
[
    {"WR": {"data": "qwe"}},
    {"QU": {"data": "asd"}},
    {"QA": {"data": "end"}},
    {"WO": {"data": "hasd"}},
    {"NO": {"data": "qwer"}}
]
}

因此,这是一种尝试重新考虑原始建模及其意图的方法。但我没有测试(我想知道)所有涉及的工具是否会保留原始JSON数组的顺序。

答案 9 :(得分:1)

在现实世界中,应用程序几乎总是具有要与JSON进行序列化/反序列化的java bean或域。它已经提到JSON对象规范不保证顺序,对该行为的任何操作都不能证明要求。 我在我的应用程序中有相同的场景,我需要保留顺序只是为了解读可读性目的。我使用标准的jackson方式将我的java bean序列化为JSON:

Object object = getObject();  //the source java bean that needs conversion
String jsonString = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(object);

为了使json具有一组有序元素,我只在我用于转换的Java bean中使用JSON属性注释。以下示例:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","phone","city","id"})
public class SampleBean implements Serializable {
    private int id;
    private String name:
    private String city;
    private String phone;

    //...standard getters and setters
}

上面使用的getObject():

public SampleBean getObject(){
    SampleBean bean  = new SampleBean();
    bean.setId("100");
    bean.setName("SomeName");
    bean.setCity("SomeCity");
    bean.setPhone("1234567890");
    return bean;
}

输出显示根据Json属性顺序注释:

{
    name: "SomeName",
    phone: "1234567890",
    city: "SomeCity",
    id: 100
}

答案 10 :(得分:0)

你可以使用

toString(JSONArray names, JSONArray ja)

它使用提供的名称列表从 JSONObjects 的 JSONArray 生成逗号分隔的文本,其顺序与 json 数组相同。

参考:https://stleary.github.io/JSON-java/org/json/CDL.html#toString-org.json.JSONArray-org.json.JSONArray-

答案 11 :(得分:0)

Underscore-java在读取json时保持元素顺序。我是该项目的维护者。

String json = "{\n"
      + "    \"items\":\n"
      + "    [\n"
      + "        {\n"
      + "            \"WR\":\"qwe\",\n"
      + "            \"QU\":\"asd\",\n"
      + "            \"QA\":\"end\",\n"
      + "            \"WO\":\"hasd\",\n"
      + "            \"NO\":\"qwer\"\n"
      + "        }\n"
      + "    ]\n"
      + "}";
System.out.println(U.fromJson(json));

// {items=[{WR=qwe, QU=asd, QA=end, WO=hasd, NO=qwer}]}

答案 12 :(得分:0)

尝试使用CsvSchema而不是使用jsonObject,而是更轻松地将对象直接转换为csv

CsvSchema schema = csvMapper.schemaFor(MyClass.class).withHeader();
        csvMapper.writer(schema).writeValueAsString(myClassList);

并保留您的pojo中包含 @JsonPropertyOrder 的订单ID

答案 13 :(得分:0)

你的例子:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
        ...
    ]
}

添加元素“itemorder”

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
        ...
    ],
    "itemorder":["WR","QU","QA","WO","NO"]
}

此代码生成所需的输出而没有列标题行:

JSONObject output = new JSONObject(json);
JSONArray docs = output.getJSONArray("data");
JSONArray names = output.getJSONArray("itemOrder");
String csv = CDL.toString(names,docs);

答案 14 :(得分:0)

您可以使用以下代码对JSON数组执行自定义ORDERED序列化和反序列化(此示例假定您要对字符串进行排序,但可以应用于所有类型):

<强>序列化

JSONArray params = new JSONArray();
int paramIndex = 0;

for (String currParam : mParams)
{
    JSONObject paramObject = new JSONObject();
    paramObject.put("index", paramIndex);
    paramObject.put("value", currParam);

    params.put(paramObject);
    ++paramIndex;
}

json.put("orderedArray", params);

<强>反序列化

JSONArray paramsJsonArray = json.optJSONArray("orderedArray");
if (null != paramsJsonArray)
{
    ArrayList<String> paramsArr = new ArrayList<>();
    for (int i = 0; i < paramsJsonArray.length(); i++)
    {
        JSONObject param = paramsJsonArray.optJSONObject(i);
        if (null != param)
        {
            int paramIndex = param.optInt("index", -1);
            String paramValue = param.optString("value", null);

            if (paramIndex > -1 && null != paramValue)
            {
                paramsArr.add(paramIndex, paramValue);
            }
        }
    }
}

答案 15 :(得分:0)

patchFor(回答@gary):

$ git diff JSONObject.java                                                         
diff --git a/JSONObject.java b/JSONObject.java
index e28c9cd..e12b7a0 100755
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -32,7 +32,7 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Enumeration;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -152,7 +152,9 @@ public class JSONObject {
      * Construct an empty JSONObject.
      */
     public JSONObject() {
-        this.map = new HashMap<String, Object>();
+//      this.map = new HashMap<String, Object>();
+        // I want to keep order of the given data:
+        this.map = new LinkedHashMap<String, Object>();
     }

     /**
@@ -243,7 +245,7 @@ public class JSONObject {
      * @throws JSONException
      */
     public JSONObject(Map<String, Object> map) {
-        this.map = new HashMap<String, Object>();
+        this.map = new LinkedHashMap<String, Object>();
         if (map != null) {
             Iterator<Entry<String, Object>> i = map.entrySet().iterator();
             while (i.hasNext()) {

答案 16 :(得分:0)

最安全的方法可能是覆盖用于生成输出的密钥方法:

new JSONObject(){
  @Override
  public Iterator keys(){
    TreeSet<Object> sortedKeys = new TreeSet<Object>();
    Iterator keys = super.keys();
    while(keys.hasNext()){
      sortedKeys.add(keys.next());
    }
    return sortedKeys.iterator();
  }
};

答案 17 :(得分:-1)

测试了眨眼解决方案,并且工作正常:

@Test
public void testJSONObject() {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    System.out.println(jsonObject.toString());
    assertTrue(jsonObject.toString().startsWith("{\"xxx\":"));
}

@Test
public void testWinkJSONObject() throws JSONException {
    org.apache.wink.json4j.JSONObject jsonObject = new OrderedJSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    assertEquals("{\"bbb\":\"xxx\",\"ccc\":\"xxx\",\"aaa\":\"xxx\",\"xxx\":\"xxx\"}", jsonObject.toString());
}