web-dev-qa-db-fra.com

Aucun créateur, comme construction par défaut, existe): impossible de désérialiser à partir de la valeur de l'objet (pas de créateur basé sur la propriété ou le délégué)

J'essaye de consommer une API utilisant retrofit et jackson pour la désérialisation. L'erreur présente dans le titre "Aucun créateur, comme construction par défaut, n'existe): impossible de désérialiser à partir de la valeur de l'objet (aucun créateur basé sur la propriété ou le délégué") apparaît dans le onFailure.

Voici le JSON que je veux récupérer:

{
    "data": {
        "repsol_id": "1129",
        "name": "ES-MASSAMÁ",
        "latitude": "38.763733333",
        "longitude": "-9.258619444000001",
        "address": "RUA GENERAL HUMBERTO DELGADO, LT.16",
        "post_code": "2745-280",
        "location": "QUELUZ",
        "service_store": 1,
        "service_mechanical_workshop": 0,
        "service_restaurant": 0,
        "service_wash": 1
    }
}

Voici mon HomeFragment:

onCreate(){
 viewModel.retrieveStation().observe(this, Observer {
            dataBinding.favouriteStationTxt.text = it.name
        })
}

C'est mon point de vueModèle:

class HomeViewModel @Inject constructor(
        private val stationRepository: StationRepository
) : ViewModel() {


    private val station = MutableLiveData<Station>()

    fun retrieveStation():LiveData<Station> = station


    fun loadStations(stationId:Int){
        stationRepository.getStationFromId(stationId,{ station.postValue(it)},{})
    }
}

Ceci est mon référentiel:

class StationRepository @Inject constructor(var apiManager: ApiManager) {

    fun getStationFromId(stationId:Int,onSuccess: (Station)->Unit, onError: (Exception)->Unit){
        apiManager.getStation(stationId, onSuccess,onError)
    }

}

Ceci est mon gestionnaire d'API (qui rejoint plusieurs gestionnaires d'api)

class ApiManager @Inject constructor(
        private val stationsApiManager: StationsApiManager, 
){

 fun getStation(stationId: Int, onSuccess: (Station)->Unit, onFailure: (e: Exception)->Unit){
        stationsApiManager.getStation(stationId,{onSuccess(it.data.toDomain())},onFailure)
    }

}

C'est mon StationAPiManager

class StationsApiManager  @Inject constructor(private val stationApiService: StationsApiService){

 fun getStation(stationId: Int, onSuccess: (StationResponse)->Unit, onFailure: (e: Exception)->Unit){
        stationApiService.getStation(stationId).enqueue(request(onSuccess, onFailure))
    }

 private fun <T> request(onSuccess: (T)->Unit, onFailure: (e: Exception)->Unit)= object : Callback<T> {

        override fun onFailure(call: Call<T>, t: Throwable) {
            Log.d("error",t.message)
            onFailure(Exception(t.message))
        }

        override fun onResponse(call: Call<T>, response: Response<T>) {
            Log.d("Success",response.body().toString())
            if(response.isSuccessful && response.body() != null) onSuccess(response.body()!!)
            else
                onFailure(Exception(response.message()))
        }
    }

}

Ceci est mon STationsApiService (l'URL de base est dans les saveurs)

@GET("{station_id}")
    fun getStation(@Path("station_id") stationId: Int): Call<StationResponse>

C'est ma StationResponse

class StationResponse (
        @JsonProperty("data")
        val data: Station)

Ceci est mon modèle de station

data class Station(
        val repsol_id: String,
        val name: String,
        val latitude: String,
        val longitude: String,
        val address: String,
        val post_code: String,
        val location: String,
        val service_store: Boolean,
        val service_mechanical_workshop: Boolean,
        val service_restaurant: Boolean,
        val service_wash: Boolean
)

Voici mes DataMappers:

import com.repsol.repsolmove.network.movestationsapi.model.Station as apiStation

fun apiStation.toDomain() = Station(
        repsol_id.toInt(),
        name,
        latitude.toDouble(),
        longitude.toDouble(),
        address,
        post_code,
        location,
        service_store,
        service_mechanical_workshop,
        service_restaurant,
        service_wash
)
12
José Nobre

Essayez les modèles ci-dessous. J'ai utilisé http://www.jsonschema2pojo.org/ pour créer ces modèles.

StationResponse.Java

import Java.util.HashMap;
import Java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"data"
})
public class StationResponse {

@JsonProperty("data")
private Data data;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("data")
public Data getData() {
return data;
}

@JsonProperty("data")
public void setData(Data data) {
this.data = data;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}

Data.Java

import Java.util.HashMap;
import Java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"repsol_id",
"name",
"latitude",
"longitude",
"address",
"post_code",
"location",
"service_store",
"service_mechanical_workshop",
"service_restaurant",
"service_wash"
})
public class Data {

@JsonProperty("repsol_id")
private String repsolId;
@JsonProperty("name")
private String name;
@JsonProperty("latitude")
private String latitude;
@JsonProperty("longitude")
private String longitude;
@JsonProperty("address")
private String address;
@JsonProperty("post_code")
private String postCode;
@JsonProperty("location")
private String location;
@JsonProperty("service_store")
private Integer serviceStore;
@JsonProperty("service_mechanical_workshop")
private Integer serviceMechanicalWorkshop;
@JsonProperty("service_restaurant")
private Integer serviceRestaurant;
@JsonProperty("service_wash")
private Integer serviceWash;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("repsol_id")
public String getRepsolId() {
return repsolId;
}

@JsonProperty("repsol_id")
public void setRepsolId(String repsolId) {
this.repsolId = repsolId;
}

@JsonProperty("name")
public String getName() {
return name;
}

@JsonProperty("name")
public void setName(String name) {
this.name = name;
}

@JsonProperty("latitude")
public String getLatitude() {
return latitude;
}

@JsonProperty("latitude")
public void setLatitude(String latitude) {
this.latitude = latitude;
}

@JsonProperty("longitude")
public String getLongitude() {
return longitude;
}

@JsonProperty("longitude")
public void setLongitude(String longitude) {
this.longitude = longitude;
}

@JsonProperty("address")
public String getAddress() {
return address;
}

@JsonProperty("address")
public void setAddress(String address) {
this.address = address;
}

@JsonProperty("post_code")
public String getPostCode() {
return postCode;
}

@JsonProperty("post_code")
public void setPostCode(String postCode) {
this.postCode = postCode;
}

@JsonProperty("location")
public String getLocation() {
return location;
}

@JsonProperty("location")
public void setLocation(String location) {
this.location = location;
}

@JsonProperty("service_store")
public Integer getServiceStore() {
return serviceStore;
}

@JsonProperty("service_store")
public void setServiceStore(Integer serviceStore) {
this.serviceStore = serviceStore;
}

@JsonProperty("service_mechanical_workshop")
public Integer getServiceMechanicalWorkshop() {
return serviceMechanicalWorkshop;
}

@JsonProperty("service_mechanical_workshop")
public void setServiceMechanicalWorkshop(Integer serviceMechanicalWorkshop) {
this.serviceMechanicalWorkshop = serviceMechanicalWorkshop;
}

@JsonProperty("service_restaurant")
public Integer getServiceRestaurant() {
return serviceRestaurant;
}

@JsonProperty("service_restaurant")
public void setServiceRestaurant(Integer serviceRestaurant) {
this.serviceRestaurant = serviceRestaurant;
}

@JsonProperty("service_wash")
public Integer getServiceWash() {
return serviceWash;
}

@JsonProperty("service_wash")
public void setServiceWash(Integer serviceWash) {
this.serviceWash = serviceWash;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}

}
4
Bek

Vous devez utiliser jackson-kotlin-module pour désérialiser les classes de données. Voir ici pour plus de détails.

Le message d'erreur ci-dessus correspond à ce que Jackson vous donne si vous essayez de désérialiser une valeur dans une classe de données lorsque ce module n'est pas activé ou, même si c'est le cas, lorsque le ObjectMapper qu'il utilise ne possède pas le KotlinModule enregistré. Par exemple, prenons ce code:

data class TestDataClass (val foo: String)

val jsonString = """{ "foo": "bar" }"""
val deserializedValue = ObjectMapper().readerFor(TestDataClass::class.Java).readValue<TestDataClass>(jsonString)

Cela échouera avec l'erreur suivante:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `test.SerializationTests$TestDataClass` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)

Si vous modifiez le code ci-dessus et remplacez ObjectMapper par jacksonObjectMapper (qui renvoie simplement un ObjectMapper normal avec le KotlinModule enregistré), cela fonctionne. c'est à dire.

val deserializedValue = jacksonObjectMapper().readerFor(TestDataClass::class.Java).readValue<TestDataClass>(jsonString)

Je ne suis pas sûr du côté Android, mais il semblerait que vous deviez obliger le système à utiliser la jacksonObjectMapper pour effectuer la désérialisation.

14
Yoni Gibbs

Je suis arrivé ici à la recherche de cette erreur:

No Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator

Rien à voir avec Retrofit mais si vous utilisez Jackson, cette erreur a été résolue en ajoutant un constructeur par défaut à la classe en lançant l'erreur. Plus ici: https://www.baeldung.com/jackson-exception

7
Fede Mika

Je sais que ceci est un ancien post, mais pour toute personne utilisant Retrofit, cela peut être utile.

Si vous utilisez les classes Retrofit + Jackson + Kotlin + Data, vous avez besoin des éléments suivants:

  1. ajoutez implement group: 'com.fasterxml.jackson.module', name: 'jackson-module-kotlin', version: '2.7.1-2' à vos dépendances pour que Jackson puisse se désérialiser en classes de données
  2. Lors de la rénovation d'un bâtiment, passez le Kotlin Jackson Mapper afin que ce dernier utilise le bon mappeur, par exemple:
    val jsonMapper = com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()

    val retrofit = Retrofit.Builder()
          ...
          .addConverterFactory(JacksonConverterFactory.create(jsonMapper))
          .build()

Remarque: Si la modification n'est pas utilisée, @Jayson Minard a une réponse plus générale à l'approche.

4
HAT

L’autre jour, j’ai eu les mêmes symptômes en utilisant JsonCreator et JsonProperty, mais j’ai eu le même message d’erreur exact. Dans mon cas, il s'est avéré que json avait un type primitif boolean, alors que mon constructeur attendait la classe de wrapper Boolean. Le framework n'a donc pas été en mesure de trouver un constructeur approprié.

Si vous utilisez nirest comme bibliothèque http, vous utiliserez également GsonObjectMapper au lieu de JacksonObjectMapper.

<!-- https://mvnrepository.com/artifact/com.konghq/unirest-object-mappers-gson -->
<dependency>
    <groupId>com.konghq</groupId>
    <artifactId>unirest-object-mappers-gson</artifactId>
    <version>2.3.17</version>
</dependency>
Unirest.config().objectMapper = GsonObjectMapper()
0
Clement