web-dev-qa-db-fra.com

Conserver l'ordre des clés JSON lors de la conversion JSON en CSV

J'utilise la bibliothèque JSON fournie ici http://www.json.org/Java/index.html pour convertir une chaîne json que je dois au format CSV . Mais le problème que j'ai est, l'ordre de les clés sont perdues après la conversion.

C'est le code de conversion:

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

C'est le contenu de "someString":

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

Voici le résultat:

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

Bien que ce que je prévois est de garder l’ordre des clés:

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

Existe-t-il un moyen d'obtenir ce résultat en utilisant cette bibliothèque? Sinon, y a-t-il une autre bibliothèque capable de conserver l'ordre des clés dans le résultat?

72
Hery

Résolu.

J'ai utilisé la bibliothèque JSON.simple à partir d'ici https://code.google.com/p/json-simple/ pour lire la chaîne JSON afin de conserver l'ordre des clés et d'utiliser la bibliothèque JavaCSV ici http: //sourceforge.net/projects/javacsv/ à convertir au format CSV.

3
Hery

Il y a plusieurs façons de le faire ... mais vous ne devriez pas.

En JSON, un objet est défini ainsi:

Un objet est un ensemble non ordonné de paires nom/valeur.

Voir http://json.org .

La plupart des implémentations de JSON ne font aucun effort pour préserver l'ordre des paires nom/valeur d'un objet, car ce n'est (par définition) pas significatif.

Si vous souhaitez que l'ordre soit préservé, vous devez redéfinir votre structure de données. par exemple.

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

ou plus simplement:

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

SUIVRE

Merci pour l'info, mais je n'ai pas d'autre choix que d'utiliser JSON dans mon application et mon application doit conserver l'ordre des clés quelle que soit la définition de l'objet JSON ... Je ne suis pas autorisé à modifier le format du fichier JSON ainsi que... 

Vous devez avoir une conversation difficile avec le créateur de cette structure de fichiers et ne vous laisseront pas la modifier. C'est/ils ont carrément tort. Vous devez les convaincre.

S'ils (vraiment} _ ne vous laisseront pas le changer:

  • Vous devriez insister pour que pas l'appelle JSON ... car ce n'est pas le cas.
  • Vous devez indiquer que vous allez devoir écrire/modifier du code spécialement pour gérer ce format "pas JSON" ... sauf si vous pouvez trouver une implémentation JSON qui préserve la commande. S'ils sont un client payant, assurez-vous qu'ils paient pour ce travail supplémentaire que vous devez faire.
  • Vous devez préciser que si le "pas JSON" doit être utilisé par un autre outil, cela posera problème. En effet, ce problème se produira encore et encore ...

Ce genre de chose est vraiment mauvais. D'une part, votre logiciel violera une spécification bien établie/établie de longue date, conçue pour promouvoir l'interopérabilité. D'un autre côté, les idiots qui ont conçu ce format de fichier boiteux (pas JSON!) Sont probablement en train de s'extasier sur les systèmes des autres, parce qu'ils ne peuvent pas gérer leurssensités.

METTRE À JOUR

Il convient également de lire ce que dit le JSON RFC (RFC 7159) à ce sujet. Voici quelques extraits:

Dans les années qui ont suivi la publication de la RFC 4627, JSON a trouvé très large utilisation. Cette expérience a révélé certains modèles qui, permise par ses spécifications, ont causé l’interopérabilité problèmes.

JavaScript Object Notation (JSON) est un format de texte pour le sérialisation des données structurées. ...

JSON peut représenter quatre types primitifs (chaînes, nombres, booléens, Et null) et deux types structurés (objets et tableaux).

Un objet est une collection unordered de zéro ou plusieurs noms/valeurs paires, où un nom est une chaîne et une valeur une chaîne, un nombre, booléen, null, objet ou tableau.

Il a été observé que les bibliothèques d'analyse JSON différaient quant à savoir si ou ils ne rendent pas visible le classement des objets par l'appel Logiciel. Implémentations dont le comportement ne dépend pas du membre la commande sera interopérable en ce sens qu'elle ne le sera pas affectés par ces différences.

87
Stephen C

C'est assez simple de maintenir l'ordre. J'ai eu le même problème avec le maintien de l'ordre de la couche DB à la couche UI.

Ouvrez le fichier JSONObject.Java. Il utilise en interne HashMap qui ne maintient pas l'ordre.

Changez-le en LinkedHashMap:

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

Cela a fonctionné pour moi. Faites-moi savoir dans les commentaires. Je suggère que la bibliothèque JSON elle-même ait une autre classe JSONObject qui maintient l'ordre, comme JSONOrderdObject.Java. Je suis très pauvre dans le choix des noms. 

43
gary

JSONObject.Java prend la carte que vous passez. Il peut s'agir de LinkedHashMap ou TreeMap et cela ne prendra hashmap que lorsque la carte est nulle.

Voici le constructeur de la classe JSONObject.Java qui effectuera la vérification de la carte.

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

Donc, avant de construire un objet json, construisez LinkedHashMap et transmettez-le ensuite au constructeur,

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

Il n'est donc pas nécessaire de changer la classe JSONObject.Java. J'espère que ça aide quelqu'un.

19
Deepak Nagaraj

Une solution plus détaillée, mais largement applicable, à ce type de problème consiste à utiliser une paire de structures de données: une liste contenant l’ordre et une carte contenant les relations.

Par exemple:

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

Vous parcourez la liste itemOrder et utilisez-la pour rechercher les valeurs de la carte. La commande est préservée, sans kludges.

J'ai utilisé cette méthode plusieurs fois.

12
Robotic Pants

Une autre solution de hacky utilisant 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
10
fabe

Apache Wink a OrderedJSONObject . Il conserve la commande lors de l'analyse de la chaîne.

6
Kirill Rakhman

Je suis tombé par hasard sur le même problème. Je pense que la solution finale utilisée par l’auteur consistait à utiliser un conteneur personnalisé:

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

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

4
Rafael

Je sais que cela est résolu et que la question a été posée il y a longtemps, mais comme je suis confronté à un problème similaire, j'aimerais donner une approche totalement différente à celle-ci:

Pour les tableaux, il est indiqué "Un tableau est une collection ordonnée de valeurs". à http://www.json.org/ - mais les objets ("Un objet est un ensemble non ordonné de paires nom/valeur.") ne sont pas ordonnés.

Je me demande pourquoi cet objet est dans un tableau - cela implique un ordre qui n'y est pas.

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

Une solution serait donc de placer les clés dans un tableau "réel" et d’ajouter les données sous forme d’objets à chaque clé comme ceci:

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

Il s’agit donc d’une approche qui tente de repenser la modélisation originale et son objectif. Mais je n’ai pas testé (et je me demande bien) si tous les outils impliqués conserveraient l’ordre de ce tableau JSON original.

2
cslotty

Dans le monde réel, une application aura presque toujours un bean ou un domaine Java à sérialiser/désérialiser en JSON. Il a déjà été mentionné que la spécification d'objet JSON ne garantit pas l'ordre et que toute manipulation de ce comportement ne justifie pas l'exigence. J'ai eu le même scénario dans mon application où je devais préserver l'ordre juste pour le but de la lisibilité. J'ai utilisé la méthode standard jackson pour sérialiser mon bean Java en JSON:

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

Afin de faire le json avec un ensemble ordonné d'éléments, j'utilise simplement l'annotation de propriété JSON dans le bean Java que j'ai utilisé pour la conversion. Un exemple ci-dessous:

@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
}

le getObject () utilisé ci-dessus:

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

La sortie montre selon l'annotation de l'ordre de la propriété Json:

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

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()) {
0
Campa

Vous pouvez utiliser le code suivant pour effectuer une sérialisation ORDERED et une désérialisation personnalisées du tableau JSON (cet exemple suppose que vous commandez des chaînes, mais que vous pouvez l'appliquer à tous les types):

Sérialisation

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

Désérialisation

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

au lieu d'utiliser jsonObject, essayez d'utiliser CsvSchema plus facilement et convertit directement l'objet en csv

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

et il mentionne l'identifiant de commande de votre pojo qui contient @JsonPropertyOrder

0
rahul

Le moyen le plus sûr est probablement de remplacer la méthode des clés utilisée pour générer une sortie:

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

Votre exemple:

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

ajouter un élément "itemorder"

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

Ce code génère la sortie souhaitée sans la ligne de titre de colonne:

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