31

「フィールド」フィールドを持つjsonを取得します。
「フィールド」にデータがある場合、オブジェクトでもある多くの (約 20) 他のフィールドを持つ OBJECT があります。問題なくデシリアライズできます。
しかし、「フィールド」にデータがない場合、それは空の ARRAY です (おかしいのはわかっていますが、それはサーバーからの応答であり、変更できません)。このようなもの:

空の場合:

"extras":[

]

いくつかのデータがあります:

"extras": {
    "22":{ "name":"some name" },
    "59":{ "name":"some other name" },
    and so on...
}

したがって、データがない場合(空の配列)、明らかに例外が発生します

Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 4319

カスタム JavaDeserializer を使用しようとしました:

public class ExtrasAdapter implements JsonDeserializer<Extras> {
    @Override
    public Extras deserialize(JsonElement json, Type typeOf,
        JsonDeserializationContext context) throws JsonParseException {
        try {
            JsonObject jsonObject = json.getAsJsonObject();
            // deserialize normally

            // the following does not work, as it makes recursive calls
            // to the same function
            //return context.deserialize(jsonObject,
            //                       new TypeToken<Object>(){}.getType());
        } catch (IllegalStateException e) {
            return null;
        }
    }
}

次の方法でjsonを読みます

Gson gsonDecoder = new GsonBuilder().registerTypeAdapter(Extras.class, new ExtrasAdapter();
// httpResponse contains json with extras filed. 
Reader reader = new InputStreamReader(httpResponse.getEntity().getContent());
Extras response = gsonDecoder.fromJson(reader, Extras.class);

20 個のフィールドすべてを手動で逆シリアル化したくありません (これがオプションであることはわかっています)。context.defaultDeserialize() などを呼び出したいだけです。
繰り返しますが、通常の json のデシリアライズ、カスタム オブジェクトの作成、カスタム TypeAdapter の登録、カスタム JavaDeserializers に問題はありません。それはすべてすでに機能しています。ARRAY と OBJECT の両方であるデータを処理するためのソリューションだけが必要です。
助けてくれてありがとう。

======================


ジョーイの答えは完璧に機能します。それはまさに私が探していたものです。ここにコードを投稿します。

public class SafeTypeAdapterFactory implements TypeAdapterFactory {
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        return new TypeAdapter<T>() {
            public void write(JsonWriter out, T value) throws IOException {
                try {
                    delegate.write(out, value);
                } catch (IOException e) {
                    delegate.write(out, null);
                }
            }
            public T read(JsonReader in) throws IOException {
                try {
                    return delegate.read(in);
                } catch (IOException e) {
                    Log.w("Adapter Factory", "IOException. Value skipped");
                    in.skipValue();
                    return null;
                } catch (IllegalStateException e) {
                    Log.w("Adapter Factory", "IllegalStateException. Value skipped");
                    in.skipValue();
                    return null;
                } catch (JsonSyntaxException e) {
                    Log.w("Adapter Factory", "JsonSyntaxException. Value skipped");
                    in.skipValue();
                    return null;
                }
            }
        };
    }
}
4

4 に答える 4

24

GSON >= 2.2.1 を使用してみて、TypeAdapterFactoryクラスを探します。

これにより、オブジェクトを逆シリアル化し、再帰を回避しながらカスタム コードを適用する前にオブジェクトを調べることができます。

使用できるgetDelegateAdapterの例を次に示します。

于 2012-05-20T23:03:51.713 に答える
3

遅刻する人にとっては、この問題を解決するために TypeAdapter を実装する必要はありませんが、そうすることは完全に有効な解決策です。

この問題に対する答えは、実際には元の質問にあります。

public class ExtrasAdapter implements JsonDeserializer<Extras> {

@Override
public Extras deserialize(JsonElement json, Type typeOf, 
          JsonDeserializationContext context) throws JsonParseException {
    try {
        JsonObject jsonObject = json.getAsJsonObject();
        // deserialize normally

        // the following does not work, as it makes recursive calls 
        // to the same function 
        //return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());
    } catch (IllegalStateException e) {
        return null;
    }
}

コメントアウトしたもの

return context.deserialize(jsonObject, new TypeToken<Object>(){}.getType());

ほぼ解決です。問題は 2 つあります。まず、jsonObject は、この関数に最初に渡された正確なオブジェクトです。

JsonObject jsonObject = json.getAsJsonObject();

したがって、それを context.deserialize() に渡すと再帰が作成され、最終的には OOM になります。ここでの解決策は、jsonObject 内のオブジェクトを解析することです。

これは 2 番目の問題につながります。つまり、2 つのものが混在しているということです。「Extras」はオブジェクト型であり、おそらくそれを支える具象クラス (およびおそらく空の配列) を持ちます。「おまけ」はマップです。「エクストラ」を「エクストラ」として解析しようとしてもうまくいきません。そのために、「エクストラ」の次の定義を提案します。

public class Extras {
    Map<String, Map<String, String>> extras;
    // you could also create a concrete class for "Extra"
    //and have this be a Map<String, Extra>
}

その場合、問題は context.deserialize で解決するために簡単になります。

上で述べたように、TypeAdatper はこの問題に対する完全に有効なソリューションです。私はそれがあなたが必要以上のものだと信じています。

于 2016-11-15T12:27:19.243 に答える