web-dev-qa-db-ja.com

JSONからCSVへの変換中にJSONキーの順序を維持する

ここで提供されている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

このライブラリを使用してこの結果を得る方法はありますか?そうでない場合、結果のキーの順序を保持する機能を提供する他のライブラリはありますか?

75
Hery

解決しました。

ここからJSON.simpleライブラリを使用しました https://code.google.com/p/json-simple/ を使用してJSON文字列を読み取り、キーの順序を維持し、ここからJavaCSVライブラリを使用します- http://sourceforge.net/projects/javacsv/ CSV形式に変換します。

3
Hery

それを行うには(ハックな)方法がありますが、そうすべきではありません。

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"}
    ]
}

フォローアップ

情報に感謝しますが、アプリケーションでJSONを使用する以外に選択肢がなく、アプリケーションはJSONオブジェクトの定義に関係なくキーの順序を維持する必要があります... JSONファイルの形式を変更することはできません同じように...

そのファイル構造を設計した人と、あなたがそれを変更させないようにする人と、厳しい会話をする必要があります。それは/彼らは明らかに間違っています。あなたを説得する必要があります

彼らが本当にあなたがそれを変更させない場合:

  • あなたはそれをJSON ...と呼ぶnotを主張する必要があります...それはそうではありません。
  • この「JSONではない」フォーマットを処理するために、コードを特別に記述/変更する必要があることを指摘する必要があります...順序を保持するJSON実装を見つけることができる場合を除きます。彼らが支払いをするクライアントであるならば、あなたがしなければならないこの余分な仕事のために彼らが支払うことを確認してください。
  • 「JSONではない」を他のツールで使用する必要がある場合、問題が発生することを指摘しておく必要があります。確かに、この問題は何度も発生します...

このようなことは本当に悪いことです。一方では、ソフトウェアは相互運用性を促進するように設計された十分に確立された/長年の仕様に違反します。一方、このラメ(JSONではない!)ファイル形式を設計したnit-witsは、おそらくシステムが対処できないtheirナンセンスのために他の人のシステムなどを怠っているでしょう。

UPDATE

また、このテーマについて JSON RFC(RFC 7159) が言っていることも読む価値があります。抜粋を次に示します。

RFC 4627の発行以来、JSONは非常に広く使用されています。この経験により、特定のパターンが明らかになりましたが、その仕様では許可されていますが、相互運用性の問題を引き起こしています。

JavaScript Object Notation(JSON)は、構造化データをシリアル化するためのテキスト形式です。 ...

JSONは、4つのプリミティブ型(文字列、数値、ブール値、null)と2つの構造化型(オブジェクトと配列)を表すことができます。

オブジェクトは、0個以上の名前/値のペアのunorderedコレクションであり、名前は文字列、値は文字列、数値、ブール値、 null、オブジェクト、または配列。

JSON解析ライブラリは、呼び出し元のソフトウェアからオブジェクトメンバーの順序を認識できるようにするかどうかが異なることが確認されています。動作がメンバーの順序に依存しない実装は、これらの違いの影響を受けないという意味で相互運用可能です。

91
Stephen C

順序を維持するのは非常に簡単です。 DBレイヤーからUIレイヤーへの順序を維持するのと同じ問題がありました。

JSONObject.Javaファイルを開きます。内部的には順序を維持しないHashMapを使用します。

LinkedHashMapに変更します。

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

これは私のために働いた。コメントで教えてください。 JSONOrderには、JSONOrderdObject.Javaのような順序を維持する別のJSONObjectクラスが必要です。私は名前を選ぶのがとても苦手です。

46
gary

JSONObject.Javaは、渡したマップを受け取ります。 LinkedHashMapまたはTreeMapの場合があり、マップがnullの場合にのみhashmapを取ります。

これは、マップのチェックを行うJSONObject.Javaクラスのコンストラクターです。

 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クラスを変更する必要はありません。誰かに役立つことを願っています。

21
Deepak Nagaraj

この種の問題に対するより冗長ではあるが広く適用可能なソリューションは、データ構造のペアを使用することです。順序付けを含むリストと、関係を含むマップです。

例えば:

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

ItemOrderリストを反復処理し、それらを使用してマップ値を検索します。順序は保持され、異常はありません。

私はこの方法を何度も使用しました。

13
Robotic Pants

リフレクトを使用した別のハッキングソリューション:

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には OrderedJSONObject があります。文字列の解析中に順序を保持します。

6
Kirill Rakhman

同じ問題につまずいたのですが、著者が使用した最終的な解決策は、カスタム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)

4
Rafael

私はこれが解決されており、質問がずっと前に尋ねられたことを知っていますが、私は同様の問題を扱っているので、これにまったく異なるアプローチをしたいと思います:

配列の場合、「配列は順序付けられた値のコレクションです」と表示されます。 at 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配列の順序を保持するかどうかはテストしていません(そして疑問に思っています)。

2
cslotty

現実の世界では、アプリケーションはほとんど常にJava Beanまたはドメインを持ち、それらはJSONとの間でシリアライズ/デシリアライズされます。 JSONオブジェクト仕様では順序が保証されておらず、その動作を操作しても要件が正当化されないことは既に述べています。読みやすさの目的のためだけに順序を保持する必要があるアプリケーションで、同じシナリオがありました。 Java BeanをJSONにシリアル化するために標準のjacksonの方法を使用しました。

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

次のコードを使用して、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);
            }
        }
    }
}
0
Muzikant

jsonObjectを使用する代わりに、CsvSchemaを簡単に使用して、オブジェクトを直接csvに変換してください

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

そして、それはあなたのpojoが持っている注文IDを保持しています@ JsonPropertyOrder

0
rahul

最も安全な方法は、おそらく出力の生成に使用されるキーメソッドをオーバーライドすることです。

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

あなたの例:

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