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

Her*_*ery 75 java csv json

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

这是转换代码:

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

这是"someString"的内容:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ]
}
Run Code Online (Sandbox Code Playgroud)

这是结果:

WO,QU,WR,QA,NO
hasd,asd,qwe,end,qwer
Run Code Online (Sandbox Code Playgroud)

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

WR,QU,QA,WO,NO
qwe,asd,end,hasd,qwer
Run Code Online (Sandbox Code Playgroud)

有什么方法可以使用这个库得到这个结果吗?如果没有,是否有任何其他库可以提供保持结果中键的顺序的功能?

Ste*_*n C 91

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

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

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

http://json.org.

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

如果要保留订单,则需要重新定义数据结构; 例如

{
    "items":
    [
        [
            {"WR":"qwe"},
            {"QU":"asd"},
            {"QA":"end"},
            {"WO":"hasd"},
            {"NO":"qwer"}
        ],
    ]
}
Run Code Online (Sandbox Code Playgroud)

或更简单地说:

{
    "items":
    [
        {"WR":"qwe"},
        {"QU":"asd"},
        {"QA":"end"},
        {"WO":"hasd"},
        {"NO":"qwer"}
    ]
}
Run Code Online (Sandbox Code Playgroud)

跟进

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

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

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

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

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

UPDATE

值得一读的还有JSON RFC(RFC 7159)在这个主题上所说的内容.以下是一些摘录:

自RFC 4627发布以来的几年中,JSON已经被广泛使用.这种体验揭示了某些模式,虽然它们的规范允许,却导致了互操作性问题.

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

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

对象是零个或多个名称/值对的无序集合,其中名称是字符串,值是字符串,数字,布尔值,空值,对象或数组.

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

  • 它是最纯粹和绝对的,但不要自欺欺人,有真实世界的场景,JSON文件需要保留其定义值的排序.以任何顺序传递和接受它们是一种基本的json方法,但我认为这不同于能够获取json文档和序列化/反序列化的能力.这是一个常见的用例,必须获取一个json文档并使用它来形成另一个标准WDDX,HTML等的文档,其中需要保留排序 (9认同)
  • @Hery - 向他们开账单:-) (6认同)
  • @YogeshSomani - "解决方案"是获取一个合适的JSON库,然后"破解"它以保留密钥顺序.有关示例,请参阅gary的答案.但是你不应该期望标准的JSON库能够做到这一点,因为这是一个鼓励滥用JSON规范的坏主意.真正的解决方案是修复您的应用程序以正确使用JSON. (3认同)
  • @AndrewNorman - 如果你所说的"需要"是真的,那么规范应该而且会被改变.实际情况是,在JSON中以其他方式表示排序很容易. (3认同)
  • JSON仅要求不按键顺序对信息进行编码。否则不是JSON。序列化的一致性是一个不同的问题。在单个应用程序中,通常可以使用通用的JSON库来保证一致性,但是也有例外,例如单个解决方案跨越多个平台或框架时。在这些情况下,您要么必须找到一种解决不一致问题的方法(引入效率低下),要么就必须找到一种确保一致性的方法。两种方法都是有效的。 (2认同)
  • *“JSON 只要求信息不要按键的顺序进行编码;否则就不是 JSON。”*。我认为你写错了。正确的说法是“JSON 不要求按照键的顺序对信息进行编码”。这两种说法有很大的不同...... (2认同)

小智 46

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

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

将其更改为LinkedHashMap:

    //this.map = new HashMap();
    this.map = new LinkedHashMap();
Run Code Online (Sandbox Code Playgroud)

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

  • svn checkout json-simple.修改文件org.json.simple.JSONObject,<code> JOSNObject从<code> .. HashMap .. </ code>扩展LinkedHashMap ... </ code>,修复导入,它对我有用.V1.1.1. (2认同)
  • 这是一个很棒的单线解决方案,我从未想过要尝试.完美的工作,我没有必要改变我的任何其他代码.+1 (2认同)
  • 哈哈真的?修改JSON库以满足您的需求?我可以想象这会在公司知识库中下降."你的mvn干净安装,下载jar,找到它,反编译它,然后用LinkedHashMap替换HashMap".这对多开发人员团队非常有用(不是). (2认同)

小智 21

JSONObject.java采取你通过的任何地图.它可能是LinkedHashMap或者仅在地图为空时TreeMap才会使用hashmap.

这是JSONObject.java将执行地图检查的类的构造函数.

 public JSONObject(Map paramMap)
  {
    this.map = (paramMap == null ? new HashMap() : paramMap);
  }
Run Code Online (Sandbox Code Playgroud)

所以在构建一个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));
Run Code Online (Sandbox Code Playgroud)

所以没有必要改变JSONObject.java课程.希望它对某人有帮助.

  • 这不适用于`org.json`库.构造一个新的`JSONObject`对象时,将一个map作为参数传递,它将始终转换为`HashMap`.构造函数首先创建一个新映射:`this.map = new HashMap <String,Object>();`,然后循环遍历映射,将每个元素复制到他的`HashMap`. (6认同)
  • 很好的答案,适用于普通的java,但不适用于Android.我检查了android中的org.json.JSONObject,很遗憾android仍然使用内部创建的hashmap.它只将paramMap中的名称/值对复制到HashMap ... :( (3认同)
  • @Deepak Nagaraj.我已经尝试过你的解决方案,但它不起作用,`JSONObject`没有被命名为我的`LinkedHashMap`.你是在使用`org.json` lib吗? (2认同)
  • 这不是“org.json”。它在内部定义了一个“HashMap”,无论您给它什么“Map”:@Deepak您能给我们指出您使用的实现吗? (2认同)
  • 这怎么不是得票最高的答案?`JSONObject(Map)` 构造函数就像一个魅力(使用 org.json:json 20080701)。事实上,在 JSON 之上强加排序可能有点麻烦,但是有真正的用例,这些用例绝不是对 JSON 数据模型的滥用;考虑将 JSON 序列化到版本控制中的文件,使用顺序来保持差异小且有意义。 (2认同)

小智 13

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

例如:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ],
    "itemOrder":
        ["WR", "QU", "QA", "WO", "NO"]
}
Run Code Online (Sandbox Code Playgroud)

您迭代itemOrder列表,并使用它们查找地图值.订购保留,没有kludges.

我多次使用过这种方法.


fab*_*abe 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
Run Code Online (Sandbox Code Playgroud)


Kir*_*man 6

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


Her*_*ery 3

解决了。

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