2016-12-03 3 views
2

は、私たちが、このJSONがあるとします。モシで汎用タイプをデシリアライズする方法は?

[ 
    { 
    "__typename": "Car", 
    "id": "123", 
    "name": "Toyota Prius", 
    "numDoors": 4 
    }, 
    { 
    "__typename": "Boat", 
    "id": "4567", 
    "name": "U.S.S. Constitution", 
    "propulsion": "SAIL" 
    } 
] 

(リストへのより多くの要素があるかもしれないが、これは2つだけ表示されます)

を私は共通のためVehicle基本クラスを使用しCarBoat POJOを持っていますフィールド:

public abstract class Vehicle { 
    public final String id; 
    public final String name; 
} 

public class Car extends Vehicle { 
    public final Integer numDoors; 
} 

public class Boat extends Vehicle { 
    public final String propulsion; 
} 

List<Vehicle>する必要があります。このJSONを解析した結果。問題は、__typenameBoatCarから区別する方法であることをJSONパーサが知っていることはないことです。 Gsonで

、私は適切な型に特定のJSONオブジェクトを解析するために供給さJsonDeserializationContextdeserialize()を使用し、次いで、これはCar又はBoatであるか否かを識別し、__typenameフィールドを調べることができるJsonDeserializer<Vehicle>を作成することができます。これは正常に動作します。

しかし、私が構築しようとしていることは、差し替え可能なJSONパーサをサポートしなければならないことです。代替パーサとしてMoshiを試してみると思いました。しかし、現時点では、この特定の問題はMoshiの文書ではうまくカバーされておらず、どのように対処するのが最善かを理解するのが難しいです。

JsonDeserializer<T>is JsonAdapter<T>と最も類似しています。ただし、fromJson()は、破壊的なAPIを持つJsonReaderを渡します。 __typenameが何であるかを知るには、私はJsonReaderイベントから手作業ですべてを解析できる必要があります。私はadapter() on the Moshi instanceと呼んで、いったん適切な具体的な型を知ったら、既存のMoshi構文解析ロジックを呼び出すことができましたが、私はJsonReaderのデータを消費し、完全なオブジェクト記述を提供できなくなります。

JsonDeserializer<Vehicle>の別のアナログはで、Vehicleを返します。しかし、私はこのメソッドに渡す簡単なことを特定することはできません。私はMoshiと型アダプタとして登録クラスに@FromJson Vehicle rideLikeTheWind(SemiParsedKindOfVehicle rawVehicle)を持っている場合、理論的には、

public class SemiParsedKindOfVehicle { 
    public final String id; 
    public final String name; 
    public final Integer numDoors; 
    public final String propulsion; 
    public final String __typename; 
} 

その後:私は考えることができる唯一のことは、すべての可能なフィールドの和集合を表すさらに別のPOJOを作成することですMoshiはJSONオブジェクトをSemiParsedKindOfVehicleインスタンスに解析し、rideLikeTheWind()を呼び出すことができます。そこで、私は__typenameを探し、タイプを特定して、CarまたはBoatを完全に構築し、そのオブジェクトを返します。

実行可能ですが、これはGsonのアプローチよりもかなり複雑です。Car/Boatシナリオは、対処する必要がある可能なデータ構造の簡単な終わりです。

Moshiでこれを処理する別の方法がありますか?

答えて

1

私が考慮しなかったことの1つは、Map<String, Object>のような汎用タイプを使用してタイプアダプタを作成できることです。それで、__typenameを検索するVehicleAdapterを作成することができます。CarBoatのインスタンスを完全に埋め込む必要があります(または、オプションでMap<String, Object>を入力とするCarおよびBoatのコンストラクタに委譲することもできます)。したがって、これはまだGsonのアプローチほど便利ではありません。さらに、何もしていない@ToJsonメソッドが必要です。それ以外の場合は、Moshiがタイプアダプタを拒否します。しかし、それ以外の場合、このJUnit4テストクラスで実証されているように動作します。

import com.squareup.moshi.FromJson; 
import com.squareup.moshi.JsonAdapter; 
import com.squareup.moshi.Moshi; 
import com.squareup.moshi.ToJson; 
import com.squareup.moshi.Types; 
import org.junit.Assert; 
import org.junit.Test; 
import java.io.IOException; 
import java.lang.reflect.Type; 
import java.util.List; 
import java.util.Map; 
import static org.junit.Assert.assertEquals; 

public class Foo { 
    static abstract class Vehicle { 
    public String id; 
    public String name; 
    } 

    static class Car extends Vehicle { 
    public Integer numDoors; 
    } 

    static class Boat extends Vehicle { 
    public String propulsion; 
    } 

    static class VehicleAdapter { 
    @FromJson 
    Vehicle fromJson(Map<String, Object> raw) { 
     String typename=raw.get("__typename").toString(); 
     Vehicle result; 

     if (typename.equals("Car")) { 
     Car car=new Car(); 

     car.numDoors=((Double)raw.get("numDoors")).intValue(); 
     result=car; 
     } 
     else if (typename.equals("Boat")) { 
     Boat boat=new Boat(); 

     boat.propulsion=raw.get("propulsion").toString(); 
     result=boat; 
     } 
     else { 
     throw new IllegalStateException("Could not identify __typename: "+typename); 
     } 

     result.id=raw.get("id").toString(); 
     result.name=raw.get("name").toString(); 

     return(result); 
    } 

    @ToJson 
    String toJson(Vehicle vehicle) { 
     throw new UnsupportedOperationException("Um, why is this required?"); 
    } 
    } 

    static final String JSON="[\n"+ 
    " {\n"+ 
    " \"__typename\": \"Car\",\n"+ 
    " \"id\": \"123\",\n"+ 
    " \"name\": \"Toyota Prius\",\n"+ 
    " \"numDoors\": 4\n"+ 
    " },\n"+ 
    " {\n"+ 
    " \"__typename\": \"Boat\",\n"+ 
    " \"id\": \"4567\",\n"+ 
    " \"name\": \"U.S.S. Constitution\",\n"+ 
    " \"propulsion\": \"SAIL\"\n"+ 
    " }\n"+ 
    "]"; 

    @Test 
    public void deserializeGeneric() throws IOException { 
    Moshi moshi=new Moshi.Builder().add(new VehicleAdapter()).build(); 
    Type payloadType=Types.newParameterizedType(List.class, Vehicle.class); 
    JsonAdapter<List<Vehicle>> jsonAdapter=moshi.adapter(payloadType); 
    List<Vehicle> result=jsonAdapter.fromJson(JSON); 

    assertEquals(2, result.size()); 

    assertEquals(Car.class, result.get(0).getClass()); 

    Car car=(Car)result.get(0); 

    assertEquals("123", car.id); 
    assertEquals("Toyota Prius", car.name); 
    assertEquals((long)4, (long)car.numDoors); 

    assertEquals(Boat.class, result.get(1).getClass()); 

    Boat boat=(Boat)result.get(1); 

    assertEquals("4567", boat.id); 
    assertEquals("U.S.S. Constitution", boat.name); 
    assertEquals("SAIL", boat.propulsion); 
    } 
} 
+1

次のバージョンのMoshiでは、多態性の逆シリアル化がよりうまくサポートされるようになりました。 (すべての詳細へのリンクについては、https://github.com/square/moshi/issues/89を参照してください)。私は今、モシのタグに従っているので、任意のすべての質問を撃つ! –

+0

@EricCochran:情報ありがとう!しかし、私が問題を誤読していない限り、その問題は多態的な直列化に関係していないようです。 – CommonsWare

+0

おそらく最も重要なことに、 'JsonAdapter.fromJsonValue(Object)'(https://github.com/square/moshi/pull/234/files)は、Mapsなどからこれらの値を作成するのに役立ちます。 –

関連する問題