Estoy usando la biblioteca JSON proporcionada aquí http://www.json.org/java/index.html para convertir una cadena json que tengo a CSV. Pero el problema que tengo es que el orden de las claves se pierde después de la conversión.
Este es el código de conversión:
JSONObject jo = new JSONObject(someString);
JSONArray ja = jo.getJSONArray("items");
String s = CDL.toString(ja);
System.out.println(s);
Este es el contenido de "someString":
{
"items":
[
{
"WR":"qwe",
"QU":"asd",
"QA":"end",
"WO":"hasd",
"NO":"qwer"
},
]
}
Este es el resultado:
WO,QU,WR,QA,NO
hasd,asd,qwe,end,qwer
Si bien lo que espero es mantener el orden de las claves:
WR,QU,QA,WO,NO
qwe,asd,end,hasd,qwer
¿Hay alguna forma de que pueda obtener este resultado usando esta biblioteca? Si no es así, ¿hay alguna otra biblioteca que proporcione la capacidad de mantener el orden de las claves en el resultado?
foo
antes que la clavebar
y, a veces,bar
antesfoo
. Esto hace que sea difícil escribir pruebas para.Respuestas:
Hay formas (hacky) de hacerlo ... pero no deberías.
En JSON, un objeto se define así:
Consulte http://json.org .
La mayoría de las implementaciones de JSON no hacen ningún esfuerzo por preservar el orden de los pares nombre / valor de un objeto, ya que (por definición) no es significativo.
Si desea conservar el orden, debe redefinir su estructura de datos; p.ej
{ "items": [ [ {"WR":"qwe"}, {"QU":"asd"}, {"QA":"end"}, {"WO":"hasd"}, {"NO":"qwer"} ], ] }
o más simplemente:
{ "items": [ {"WR":"qwe"}, {"QU":"asd"}, {"QA":"end"}, {"WO":"hasd"}, {"NO":"qwer"} ] }
SEGUIMIENTO
Necesita tener una conversación dura con quien diseñó esa estructura de archivos y no le permitirá cambiarla. Es / están completamente equivocados. Usted necesita para convencerlos.
Si realmente no te dejan cambiarlo:
Este tipo de cosas son realmente malas. Por un lado, su software violará una especificación bien establecida / de larga data que está diseñada para promover la interoperabilidad. Por otro lado, los imbéciles que diseñaron este formato de archivo poco convincente (¡no JSON!) Probablemente se estén burlando de los sistemas de otras personas, etc. porque los sistemas no pueden hacer frente a sus tonterías.
ACTUALIZAR
También vale la pena leer lo que dice JSON RFC (RFC 7159) sobre este tema. Aquí hay algunos extractos:
fuente
Mantener el orden es bastante sencillo. Tuve el mismo problema al mantener el orden de la capa DB a la capa UI.
Abra el archivo JSONObject.java. Utiliza internamente HashMap que no mantiene el orden.
Cámbielo a LinkedHashMap:
//this.map = new HashMap(); this.map = new LinkedHashMap();
Esto funcionó para mí. Házmelo saber en los comentarios. Sugiero que la biblioteca JSON en sí debería tener otra clase JSONObject que mantenga el orden, como JSONOrderdObject.java. Soy muy pobre para elegir los nombres.
fuente
this.map = new LinkedHashMap<String, Object>();
todavía no funciona. Por favor ayuda ?JSONObject.java
toma cualquier mapa que pase. Puede serLinkedHashMap
oTreeMap
yhashmap
solo tomará cuando el mapa sea nulo.Aquí está el constructor de la
JSONObject.java
clase que hará la verificación del mapa.public JSONObject(Map paramMap) { this.map = (paramMap == null ? new HashMap() : paramMap); }
Entonces, antes de construir una construcción de objeto json
LinkedHashMap
y luego pasarla al constructor de esta manera,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));
Entonces no es necesario cambiar la
JSONObject.java
clase. Espero que ayude a alguien.fuente
JSONObject
no está ordenado como myLinkedHashMap
. ¿Estás usandoorg.json
lib?org.json
.. Define unaHashMap
internamente, sea loMap
que sea que le des: @Deepak, ¿puedes señalarnos la implementación que usaste?org.json
biblioteca. Cuando construye un nuevoJSONObject
objeto, pasando un mapa como argumento, siempre se convertirá aHashMap
. El constructor primero crea un nuevo mapa:,this.map = new HashMap<String, Object>();
y luego recorre su mapa, copiando cada elemento en el suyoHashMap
.Una solución más detallada, pero ampliamente aplicable a este tipo de problema, es utilizar un par de estructuras de datos: una lista para contener el orden y un mapa para contener las relaciones.
Por ejemplo:
{ "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ], "itemOrder": ["WR", "QU", "QA", "WO", "NO"] }
Se itera la lista itemOrder y se utilizan para buscar los valores del mapa. El orden se conserva, sin errores.
He usado este método muchas veces.
fuente
Otra solución hacky usando reflect:
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
fuente
Apache Wink tiene OrderedJSONObject . Mantiene el orden mientras analiza la cadena.
fuente
Me encontré con el mismo problema, creo que la solución final utilizada por el autor consistió en usar un ContainerFactory personalizado:
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; }
ver http://juliusdavies.ca/json-simple-1.1.1-javadocs/org/json/simple/parser/JSONParser.html#parse(java.io.Reader,org.json.simple.parser.ContainerFactory)
fuente
Resuelto.
Usé la biblioteca JSON.simple desde aquí https://code.google.com/p/json-simple/ para leer la cadena JSON para mantener el orden de las claves y usar la biblioteca JavaCSV desde aquí http://sourceforge.net/ proyectos / javacsv / para convertir a formato CSV.
fuente
org.json
para convertirList<LinkedHahMap>
aJSONArray
para crear unCSV
,CSV
se crea pero no en el mismo orden que miList
. Intenté usar las dos bibliotecas que presenta aquí, pero no pude encontrar los métodos para convertir aCSV
. ¿Podría por favor dar más detalles?Sé que esto está resuelto y la pregunta se hizo hace mucho tiempo, pero como estoy lidiando con un problema similar, me gustaría darle un enfoque totalmente diferente a esto:
En el caso de matrices, dice "Una matriz es una colección ordenada de valores". en http://www.json.org/ - pero los objetos ("Un objeto es un conjunto desordenado de pares de nombre / valor") no están ordenados.
Me pregunto por qué ese objeto está en una matriz, eso implica un orden que no está allí.
{ "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ] }
Entonces, una solución sería poner las claves en una matriz "real" y agregar los datos como objetos a cada clave de esta manera:
{ "items": [ {"WR": {"data": "qwe"}}, {"QU": {"data": "asd"}}, {"QA": {"data": "end"}}, {"WO": {"data": "hasd"}}, {"NO": {"data": "qwer"}} ] }
Así que este es un enfoque que intenta repensar el modelado original y su intención. Pero no he probado (y me pregunto) si todas las herramientas involucradas preservarían el orden de esa matriz JSON original.
fuente
En el mundo real, una aplicación casi siempre tendrá un bean o dominio Java que se va a serializar / deserializar a / desde JSON. Ya se mencionó que la especificación del objeto JSON no garantiza el orden y cualquier manipulación de ese comportamiento no justifica el requisito. Tuve el mismo escenario en mi aplicación en el que necesitaba preservar el orden solo por motivos de legibilidad. Usé la forma estándar de jackson para serializar mi bean java a JSON:
Object object = getObject(); //the source java bean that needs conversion String jsonString = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(object);
Para hacer el json con un conjunto ordenado de elementos, solo uso la anotación de propiedad JSON en el bean Java que usé para la conversión. Un ejemplo a continuación:
@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 }
el getObject () utilizado anteriormente:
public SampleBean getObject(){ SampleBean bean = new SampleBean(); bean.setId("100"); bean.setName("SomeName"); bean.setCity("SomeCity"); bean.setPhone("1234567890"); return bean; }
La salida se muestra según la anotación de orden de propiedad de Json:
{ name: "SomeName", phone: "1234567890", city: "SomeCity", id: 100 }
fuente
La forma más segura es probablemente anular el método de claves que se utiliza para generar resultados:
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(); } };
fuente
patchFor (respuesta @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()) {
fuente
Puede utilizar el siguiente código para realizar una serialización ORDENADA personalizada y una deserialización de JSON Array (este ejemplo asume que está ordenando cadenas pero se puede aplicar a todos los tipos):
Publicación por entregas
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);
Deserialización
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); } } } }
fuente
Tu ejemplo:
{ "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ... ] }
agregar un elemento "itemorder"
{ "items": [ { "WR":"qwe", "QU":"asd", "QA":"end", "WO":"hasd", "NO":"qwer" }, ... ], "itemorder":["WR","QU","QA","WO","NO"] }
Este código genera la salida deseada sin la línea de título de la columna:
JSONObject output = new JSONObject(json); JSONArray docs = output.getJSONArray("data"); JSONArray names = output.getJSONArray("itemOrder"); String csv = CDL.toString(names,docs);
fuente
en lugar de usar jsonObject, intente usar CsvSchema de manera más fácil y convierta directamente el objeto a csv
y mentaliza la identificación de la orden que su pojo tiene @JsonPropertyOrder en ella
fuente
Underscore-java mantiene los pedidos de elementos mientras lee json. Soy el mantenedor del proyecto.
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}]}
fuente
Probé la solución de guiño y funcionó bien:
@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()); }
fuente