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

我正在使用这里提供的JSON库http://www.json.org/java/index.html来转换一个JSONstring我必须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 

有什么办法可以使用这个库的结果吗? 如果不是的话,还有没有其他的库可以提供在结果中保留键的顺序的function?

有(哈克)的方式来做到这一点…但你不应该。

在JSON中,这样定义一个对象:

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

http://json.org

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

如果你想要保存命令,你需要重新定义你的数据结构; 例如

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

或者更简单:

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

跟进

感谢您的信息,但我没有select,只能在我的应用程序中使用JSON和我的应用程序需要保持键的顺序,无论JSON对象的定义…我不能改变JSON文件的格式以及…

你需要和devise这个文件结构的人进行一番艰苦的谈话,而不会让你改变它。 这是/他们是明显的错误。 你需要说服他们。

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

  • 你应该坚持不要把它叫做JSON …'因为它不是。
  • 你应该指出,你将不得不写/修改代码来处理这个“不是JSON”格式……除非你能find一些JSON实现来保存这个命令。 如果他们是付费客户,确保他们为这项额外的工作付出代价。
  • 你应该指出,如果“不是JSON”需要被其他工具所使用,那将是有问题的。 确实,这个问题会一遍又一遍地发生…

这种事情真的很糟糕。 一方面,您的软件将违反旨在促进互操作性的既定/长期规范。 另一方面,devise这种蹩脚的(而不是JSON!)文件格式的尼斯可能会把别人的系统等排除在外,因为系统无法应付他们的废话。

UPDATE

阅读JSON RFC(RFC 7159)对这个主题的评价也是值得的。 以下是一些摘录:

自从RFC 4627发布以来,JSON已经被广泛使用。 这个经验揭示了某些模式,尽pipe它的规范允许,但却导致了互操作性问题。

JavaScript对象表示法(JSON)是用于结构化数据序列化的文本格式。 …

JSON可以表示四种基本types(string,数字,布尔值和空值)以及两种结构化types(对象和数组)。

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

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

维持秩序非常简单。 维护从数据库层到UI层的顺序也有同样的问题。

打开JSONObject.java文件。 它在内部使用不保持顺序的HashMap。

将其更改为LinkedHashMap:

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

这对我有效。 让我知道在评论中。 我build议JSON库本身应该有另一个维护顺序的JSONObject类,比如JSONOrderdObject.java。 我select名字的能力很差。

JSONObject.java接受你传递的任何地图。 它可能是LinkedHashMap或者TreeMap ,只有当map为null时才会使用hashmap

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

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

所以在构build一个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类。 希望它有助于某人。

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

例如:

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

您迭代itemOrder列表,并使用这些查找地图值。 订购被保留,没有任何kludges。

我多次使用这个方法。

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

 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 

Apache Wink有OrderedJSONObject 。 它parsingstring时保持顺序。

我知道这个问题已经解决了,而且很久以前就提出了这个问题,但是当我处理类似的问题时,我想给出一个完全不同的方法:

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

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

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

所以一个解决scheme是将键放在一个“真实”的数组中,并将数据作为对象添加到每个键,如下所示:

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

所以这是一种试图重新思考原始模型及其意图的方法。 但是我还没有testing(和我想知道)如果所有涉及的工具将保留原来的JSON数组的顺序。

只是偶然发现了同样的问题,我相信作者使用的最终解决scheme是使用一个自定义的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);

最安全的方法可能是用来生成输出的重写键方法:

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

patchFor(answer @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()) { 

您可以使用以下代码来执行JSON数组的自定义ORDERED序列化和反序列化(此示例假定您要订购string,但可以应用于所有types):

序列化

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

你的例子:

 { "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); 

在现实世界中,应用程序几乎总是具有将被序列化/去序列化到/来自JSON的java bean或域。 它已经提到JSON对象规范不保证顺序,任何对该行为的操作都不能certificate这个要求。 我在我的应用程序中有相同的场景,我需要维护秩序只是为了解读可读性的目的。 我使用标准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 } 

testing了眨眼解决scheme,工作正常:

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