Cum se deserializează câmpurile de interfață folosind Jackson’s objectMapper? (Programare, Java, Json, Jackson)

Naman a intrebat.

ObjectMapper‘s readValue(InputStream in, Class<T> valueType) necesită Class. Dar cum o pot folosi dacă clasa pe care o transmit în mod intern are o interfață ca membru de date.

Deși pot înțelege motivul din spatele acestei excepții, deoarece Jackson nu obține clasa concretă a interfeței interne a clasei transmise, dar întrebarea mea este cum să o rezolv? Cum o dezerializez? Clasa pe care încerc să o deserializez este:

class BaseMetricImpl<N> implements Metric<N> {
    protected MetricValueDescriptor descriptor;
}

Aici MetricValueDescriptor este o interfață, așa că îmi dă următoarea eroare: –

com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of MetricValueDescriptor, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: [email protected]; line: 1, column: 2] (through reference chain: SingleValueMetricImpl["descriptor"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:624)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:115)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:2793)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1989)

2 răspunsuri
Hari Menon

Jackson, în mod evident, nu poate construi clasa MetricValueDescriptor obiectul, deoarece este o interfață. Va trebui să aveți informații suplimentare în json și în ObjectMapper pentru a-i spune lui jackson cum să construiască un obiect din el. Iată o modalitate de a face acest lucru, presupunând că MVDImpl este o clasă concretă care implementează MetricValueDescriptor:

Îi puteți spune lui Jackson informațiile necesare despre tip printr-un câmp din json, de exemplu "type". Pentru a face acest lucru, trebuie să folosiți JsonTypeInfo și JsonSubTypes în interfața dumneavoastră. De exemplu,

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type")
@JsonSubTypes({
    @Type(value = MVDImpl.class, name = "mvdimpl") })
interface MetricValueDescriptor
{
   ...
}

Va trebui să adăugați o "type":"mvdimpl" câmp în json.

Aveam de gând să vă îndrum către documentul oficial pentru mai multe informații, dar apoi am găsit un blog excelent care acoperă acest subiect – Deserializarea JSON cu Jackson. Acesta acoperă acest subiect destul de cuprinzător și cu exemple. Așa că ar trebui neapărat să îl citiți dacă aveți nevoie de mai multe personalizări.

Comentarii

  • Mă confrunt cu aceeași problemă, însă, în cazul meu, se întâmplă că Super-class nu-mi aparține, nu poate fi modificat. În acest caz, cum putem permite ca aplicația Jackson hartă în dreapta Sub-class? –  > Por lvarayut.
  • @lvarayut Puteți folosi Jackson Mixin Annotations pentru a adăuga adnotări la clasele nemodificabile. –  > Por mucaho.
  • O altă modalitate (puțin mai implicată) este de a utiliza așa-numita „default typing”, activată prin intermediul ObjectMapper. Aceasta va permite includerea activată a informațiilor de tip pentru categorii de clase, fără adnotări. În mod implicit există câteva opțiuni, cum ar fi „toate tipurile neconcrete”, „toate tipurile nefinale”, dar puteți, de asemenea, să vă implementați propria strategie. –  > Por StaxMan.
  • @Hari Shankar Am făcut acest lucru și a funcționat parțial. Singura problemă este că am o superclasă, nu o interfață, cu câteva proprietăți și cu getteri și setteri respectivi. Apoi, când fac acest lucru, obțin clasa corectă, dar proprietățile moștenite nu sunt populate. Aveți vreo idee de ce? Vă mulțumesc anticipat. –  > Por ViniciusPires.
  • Acest lucru creează o dependență ciclică între tipul super și tipurile derivate? –  > Por nishant.
xbakesx

Văd că se poate proceda în două moduri, dar ambele necesită crearea manuală a unei clase concrete care să implementeze interfața dvs.

  1. Folosiți răspunsul lui @Hari Menon și utilizați @JsonSubTypes. Acest lucru funcționează dacă puteți introduce un câmp de tip sau altceva pentru a declanșa ce implementare să utilizați.
  2. Utilizați @JsonDeserialize pentru a-i spune lui jackson ce clasă concretă folosește în mod implicit.
@JsonDeserialize(as = MVDImpl.class)
interface MetricValueDescriptor
{
   ...
}

Iată o explicație mai amănunțită: https://zenidas.wordpress.com/recipes/jackson-deserialization-of-interfaces/

Și documentația: https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html