web-dev-qa-db-ja.com

Gsonシリアル化でフィールドシーケンスを維持する方法

Gson.toJson(Object object)は、オブジェクトのフィールドがランダムに拡散されたJSONコードを生成するようです。どういうわけかフィールドの順序を修正する方法はありますか?


    public class Foo {
            public String bar;
            public String baz;

            public Foo( String bar, String baz ) {
                    this.bar = bar;
                    this.baz = baz;
            }
    }

    Gson gson = new Gson();
    String jsonRequest = gson.toJson(new Foo("bar","baz"));

    // jsonRequest now can be { "bar":"bar", "baz":"baz" } (which is correct)
    //             and can be { "baz":"baz", "bar":"bar" } (which is a wrong sequence)

ありがとう。

38

GSONがフィールドオーダーの定義をサポートしていない場合は、サポートしている他のライブラリがあります。 Jackson は、たとえば@JsonPropertyOrderでこれを定義できます。独自のカスタムシリアライザーを指定する必要があるのは、私にとっては大変な作業のようです。

そして、はい、JSON仕様に従って、アプリケーションはフィールドの特定の順序を期待すべきではないことに同意します。

22
StaxMan

カスタムJSONシリアライザーを作成する必要があります。

例えば。

public class FooJsonSerializer implements JsonSerializer<Foo> {

    @Override
    public JsonElement serialize(Foo foo, Type type, JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        object.add("bar", context.serialize(foo.getBar());
        object.add("baz", context.serialize(foo.getBaz());
        // ...
        return object;
    }

}

次のように使用します。

Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class, new FooJsonSerializer()).create();
String json = gson.toJson(foo);
// ...

これにより、シリアライザーで指定した順序が維持されます。

以下も参照してください。

30
BalusC

実際、Gson.toJson(Object object)はランダムな順序でフィールドを生成しません。結果のjsonの順序は、フィールド名のリテラルシーケンスに依存します。

同じ問題があり、クラス内のプロパティ名の文字順で解決されました。

質問の例では、常に次のjsonRequestが返されます。

{ "bar":"bar", "baz":"baz" }

特定の順序にするには、フィールドの名前を変更する必要があります。例:bazを最初にしたい場合は、barを使用します。

public class Foo {
    public String f1_baz;
    public String f2_bar;

    public Foo ( String f1_baz, String f2_bar ) {
        this.f1_baz = f1_baz;
        this.f2_bar = f2_bar;
    }
}

jsonRequestは{ "f1_baz ":"baz", "f2_bar":"bar" }

4
YouQam

これが、指定されたディレクトリのjsonテキストファイルをループし、ソートされたバージョンでそれらの上に上書きするための私の解決策です。

private void standardizeFormat(File dir) throws IOException {
    File[] directoryListing = dir.listFiles();
    if (directoryListing != null) {
        for (File child : directoryListing) {
            String path = child.getPath();
            JsonReader jsonReader = new JsonReader(new FileReader(path));

            Gson gson = new GsonBuilder().setPrettyPrinting().registerTypeAdapter(LinkedTreeMap.class, new SortedJsonSerializer()).create();
            Object data = gson.fromJson(jsonReader, Object.class);
            JsonWriter jsonWriter = new JsonWriter(new FileWriter(path));
            jsonWriter.setIndent("  ");
            gson.toJson(data, Object.class, jsonWriter);
            jsonWriter.close();
        }
    }
}

private class SortedJsonSerializer implements JsonSerializer<LinkedTreeMap> {
    @Override
    public JsonElement serialize(LinkedTreeMap foo, Type type, JsonSerializationContext context) {
        JsonObject object = new JsonObject();
        TreeSet sorted = Sets.newTreeSet(foo.keySet());
        for (Object key : sorted) {
            object.add((String) key, context.serialize(foo.get(key)));
        }
        return object;
    }
}

Typeが単にObjectである場合にGsonがLinkedTreeMapを使用するという事実に依存しているため、かなりハッキーです。これは、おそらく保証されない実装の詳細です。とにかく、それは私の短命の目的には十分です...

2
matt burns