web-dev-qa-db-ja.com

SpringおよびJsonTypeInfoアノテーションを使用してJSONをポリモーフィックオブジェクトモデルにデシリアライズする

Spring MVC(v3.2.0.RELEASE)Webアプリケーションに次のオブジェクトモデルがあります。

public class Order {
  private Payment payment;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.WRAPPER_OBJECT)
@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class)
public interface Payment {}


@JsonTypeName("creditCardPayment")
public class CreditCardPayment implements Payment {}

OrderクラスをJSONにシリアル化すると、次の結果が得られます(まさにこれが必要です)。

{
  "payment" : {
    "creditCardPayment": {
      ...
    } 
}

残念ながら、上記のJSONを使用してオブジェクトモデルにデシリアライズしようとすると、次の例外が発生します。

JSONを読み取れませんでした:タイプID 'creditCardPayment'を[ソース:org.Apache.catalina.connector.CoyoteInputStream@19629355;の[単純なタイプ、クラス支払い]のサブタイプに解決できませんでした。行:1、列:58](参照チェーン:Order ["payment"]を使用);ネストされた例外はcom.fasterxml.jackson.databind.JsonMappingException:型ID 'creditCardPayment'を[ソース:org.Apache.catalina.connector.CoyoteInputStream@19629355;の[単純型、クラス支払い]のサブタイプに解決できませんでした。行:1、列:58](参照チェーン:Order ["payment"]を介して)

私のアプリケーションは、次のようにSpring JavaConfを介して構成されます。

@Configuration
@EnableWebMvc
public class AppWebConf extends WebMvcConfigurerAdapter {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
        return objectMapper;
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJacksonMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    @Bean
    public Jaxb2RootElementHttpMessageConverter jaxbMessageConverter() {
        return new Jaxb2RootElementHttpMessageConverter();
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(jaxbMessageConverter());
        converters.add(mappingJacksonMessageConverter());
    }
}

テストのために、2つのメソッドを持つコントローラーがあります。1つはHTTP GETリクエストのOrderを返し(これは機能します)、もう1つはHTTP POST(この1つは失敗します).

@Controller
public class TestController {

    @ResponseBody
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public Order getTest() {}

    @RequestMapping(value = "/test", method = RequestMethod.POST)
    public void postTest(@RequestBody order) {}

}

SOに関するさまざまな議論からのすべての提案を試しましたが、これまでのところ運がありませんでした。

23
grigori

注釈を使用する代わりにObjectMapper.registerSubtypesを使用してサブタイプを登録してください

12
ragnor

メソッドregisterSubtypes()は機能します!

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
  //...
}

public class Point implements Geometry{
  //...
}
public class Polygon implements Geometry{
  //...
}
public class LineString implements Geometry{
  //...
}

GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);

try {
    geojson=mapper.readValue(source, GeoJson.class);            
} catch (IOException e) {
    e.printStackTrace();
}

注1:インターフェースと実装クラスを使用します。 jacksonに実装クラスごとにクラスをデシリアライズさせたい場合は、ObjectMapperの「registerSubtypes」メソッドを使用してすべてを登録する必要があります。

注2:さらに、インターフェースで注釈として「@JsonTypeInfo(use = JsonTypeInfo.Id.NAME、include = JsonTypeInfo.As.PROPERTY、property = "type")」を使用します。

マッパーがPOJOの値をJSONとして書き込むときに、プロパティの順序を定義することもできます。

これは、以下の注釈を使用して実行できます。

@JsonPropertyOrder({"type","crs","version","features"})
public class GeoJson {

    private String type="FeatureCollection";
    private List<Feature> features;
    private String version="1.0.0";
    private CRS crs = new CRS();
    ........
}

お役に立てれば!

5

Dropwizardベースのサービスの作業中に同様の問題が発生しました。 dropwizardコードが機能するのと同じように物事が機能しなかった理由を完全には理解していませんが、元の投稿のコードが機能しない理由を知っています。 @JsonSubTypesには、単一の値ではなく、サブタイプの配列が必要です。したがって、行を置き換えると...

@JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class)

と...

@JsonSubTypes({ @JsonSubTypes.Type(name = "creditCardPayment", value = CreditCardPayment.class) })

あなたのコードが機能すると信じています。

これと同じエラーメッセージがポップアップしている場合は、検出されているサブタイプに問題がある可能性があります。上記のような行を追加するか、@JsonTypeNameタグを含むクラスの検出に関する問題を探してみてください。

3
Aaron Cooley

同じエラーが発生し、(CreditCardPaymentがクラス名を使用する代わりに)以下のJSONに相当するものをデシリアライザーの入力として使用し、機能しました。

{
    "type": "CreditCardPayment", 
    ...
}
0
dibs

Rashminの答えはうまくいき、私はcom.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id into a subtype of BlahregisterSubtypesを使用する必要のない問題。できることは、親クラスに次の注釈を追加することです。

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "type")

違いはJsonTypeInfo.Id.CLASS の代わりに JsonTypeInfo.Id.NAME。欠点は、作成されたJSONに完全な名前空間を含むクラス名全体が含まれることです。利点は、サブタイプの登録について心配する必要がないことです。

0
herrtim

私の場合、defaultImpl = SomeClass.classを@JsonTypeInfoに変換し、SomeClass2.classに変換しようとしていた

0
Anand Rockzz