不存在任何创建者(如默认构造),无法从对象值反序列化(不存在基于委托或基于属性的创建者

时间:2018-11-07 14:29:25

标签: android kotlin jackson retrofit2

我正在尝试使用经过改型和杰克逊反序列化的API。标题为“不存在任何创建者,就像默认构造一样)中出现的错误:无法从对象值反序列化(没有基于委托或基于属性的创建者”出现在onFailure中。

这是我要提取的JSON:

{
    "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
    }
}

这是我的HomeFragment:

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

这是我的viewModel:

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)},{})
    }
}

这是我的存储库:

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

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

}

这是我的API管理器(加入了多个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)
    }

}

这是我的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()))
        }
    }

}

这是我的STationsApiService(基本URL在风格中)

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

这是我的StationResponse

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

这是我的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
)

这是我的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
)

22 个答案:

答案 0 :(得分:7)

您需要使用jackson-kotlin-module来反序列化数据类。有关详细信息,请参见here

如果您尝试在未启用该模块的情况下将某个值反序列化为数据类,或者即使该模块不使用ObjectMapper时,Jackson也会提供上述错误消息KotlinModule已注册。例如,使用以下代码:

data class TestDataClass (val foo: String)

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

这将失败,并出现以下错误:

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)

如果您更改了上面的代码,并用ObjectMapper替换了jacksonObjectMapper(它简单地返回了注册了ObjectMapper的普通KotlinModule),则可以正常工作。即

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

我不确定Android方面的问题,但看来您需要让系统使用jacksonObjectMapper进行反序列化。

答案 1 :(得分:5)

由于提到的错误,该类没有默认的构造函数。

https://github.com/allinurl/goaccess/issues/1133添加到实体类应该对其进行修复。

答案 2 :(得分:4)

我通过添加一个无参数的承包商来解决了这个问题。如果您使用的是 Lombok ,则只需添加key批注:

@NoArgsConstructor

答案 3 :(得分:4)

我有一个类似的问题(使用Jackson,lombok,gradle)和一个没有args构造函数的POJO-解决方案是添加

lombok.anyConstructor.addConstructorProperties=true

lombok.config 文件

答案 4 :(得分:3)

尝试以下型号。我用http://www.jsonschema2pojo.org/创建了这些模型。

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);
}

}

答案 5 :(得分:2)

扩展Yoni Gibbs的答案,如果您在使用改造的Android项目中并配置了Jackson的序列化,则可以执行这些操作,以使反序列化可以像kotlin的数据类一样工作。

在构建gradle导入中:

implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.11.+"

然后,您实施改造:

val serverURL = "http://localhost:8080/api/v1"

val objectMapper = ObjectMapper()
objectMapper.registerModule(KotlinModule())
//Only if you are using Java 8's Time API too, require jackson-datatype-jsr310
objectMapper.registerModule(JavaTimeModule())

Retrofit.Builder()
    .baseUrl(serverURL)
    .client(
        OkHttpClient.Builder()
           .readTimeout(1, TimeUnit.MINUTES)//No mandatory
            .connectTimeout(1, TimeUnit.MINUTES)//No mandatory
            .addInterceptor(UnauthorizedHandler())//No mandatory
            .build())
    .addConverterFactory(
                JacksonConverterFactory.create(objectMapper)
            )
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .build()

数据类:

@JsonIgnoreProperties(ignoreUnknown = true)
data class Task(val id: Int,
                val name: String,
                @JsonSerialize(using = LocalDateTimeSerializer::class)
                @JsonDeserialize(using = LocalDateTimeDeserializer::class)
                val specificDate: LocalDateTime?,
                var completed: Boolean,
                val archived: Boolean,
                val taskListId: UUID?

答案 6 :(得分:2)

只需要添加 @NoArgsConstructor 就可以了。

答案 7 :(得分:1)

如果使用的是LOMBOK。 如果没有,请创建一个文件lombok.config并添加此行。

if os.getenv('GAE_APPLICATION', None):
    # Running on production App Engine, so connect to Google Cloud SQL using
    # the unix socket at /cloudsql/<your-cloudsql-connection string>
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '/cloudsql/[YOUR-CONNECTION-NAME]',
            'USER': '[YOUR-USERNAME]',
            'PASSWORD': '[YOUR-PASSWORD]',
            'NAME': '[YOUR-DATABASE]',
        }
    }
else:
    # Running locally so connect to either a local MySQL instance or connect to
    # Cloud SQL via the proxy. To start the proxy via command line:
    #
    #     $ cloud_sql_proxy -instances=[INSTANCE_CONNECTION_NAME]=tcp:3306
    #
    # See https://cloud.google.com/sql/docs/mysql-connect-proxy
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',
            'PORT': '3306',
            'NAME': '[YOUR-DATABASE]',
            'USER': '[YOUR-USERNAME]',
            'PASSWORD': '[YOUR-PASSWORD]',
        }
    }

答案 8 :(得分:1)

我在这里搜索此错误:

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

与Retrofit无关,但是如果您使用的是Jackson,则可以通过向引发错误的类中添加默认构造函数来解决此错误。 此处更多内容:https://www.baeldung.com/jackson-exception

答案 9 :(得分:0)

如果您在pojo /模型上使用lombok,请确保您具有这些注释

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor

它可能会有所不同,但请确保getter,setter尤其是No Arg构造函数

答案 10 :(得分:0)

使用Lombok Builder时,会出现上述错误。

 @JsonDeserialize(builder = StationResponse.StationResponseBuilder.class)
 public class StationResponse{
   //define required properties 
 }     

 @JsonIgnoreProperties(ignoreUnknown = true)
 @JsonPOJOBuilder(withPrefix = "")
 public static class StationResponseBuilder {}

参考:https://projectlombok.org/features/Builder和杰克逊

答案 11 :(得分:0)

我将rescu与Kotlin一起使用,并通过使用@ConstructorProperties解决了该问题

    data class MyResponse @ConstructorProperties("message", "count") constructor(
        val message: String,
        val count: Int
    )

Jackson使用@ConstructorProperties。这也应该修复Lombok @Data。

答案 12 :(得分:0)

我可以借助@JacksonProperty注释在Kotlin中解决此问题。上述情况的用法示例为:

import com.fasterxml.jackson.annotation.JsonProperty
...
data class Station(
     @JacksonProperty("repsol_id") val repsol_id: String,
     @JacksonProperty("name") val name: String,
...

答案 13 :(得分:0)

如果您将Unirest用作http库,则也可以使用GsonObjectMapper代替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()

答案 14 :(得分:0)

前几天,在使用JsonCreator和JsonProperty时,我有相同的症状,但是得到了完全相同的错误消息。就我而言,事实证明json具有原始类型boolean,而我的构造函数正在等待包装器类Boolean。因此,该框架无法找到合适的构造函数。

答案 15 :(得分:0)

只需指出,此answer提供了更好的解释。
基本上您可以同时拥有@Getter@NoArgConstructor
或者让Lombok使用@ConstructorProperties文件重新生成lombok.config
或使用-parameters标志编译Java项目,
或者让杰克逊使用龙目岛的@Builder

答案 16 :(得分:0)

我遇到了这个问题,并使用下面的代码修复了该问题。

@Configuration
open class JacksonMapper {

    @Bean
    open fun mapper(): ObjectMapper {
        val mapper = ObjectMapper()
        ...

        mapper.registerModule(KotlinModule())
        return mapper
    }
}

答案 17 :(得分:0)

我的问题原因在我看来非常罕见,不确定在相同条件下是否还有其他人得到错误,我通过比较以前的提交找到了原因,在这里:

通过我的build.gradle我正在使用这两个编译器选项,并注释掉此行可解决此问题

//compileJava.options.compilerArgs = ['-Xlint:unchecked','-Xlint:deprecation']

答案 18 :(得分:0)

在下面的用例中遇到同样的错误。

我尝试使用 sprint boot(2.0.0 Snapshot Version) 到达 Rest(Put mapping) 端点,而在相应的 bean 中没有 default 构造函数。

但使用最新的 Spring Boot 版本(2.4.1 版本),同一段代码可以正常工作。

因此在最新版本的 Spring Boot 中不再需要 bean 默认构造函数

答案 19 :(得分:0)

我遇到了同样的错误,问题是我的模型没有实现Serializable,所以也检查一下,可能会有所帮助因为这也是原因之一

答案 20 :(得分:0)

在类中添加构造函数 要么 如果您使用的是 pojo 添加 @NoArgsConstructor @AllArgsConstructor

还包括 JsonProperty - @JsonProperty("name") private List<Employee> name;

答案 21 :(得分:0)

贝克的回答是正确的。

但是如果有人试图在 restcontroller 中使用不可变类,即他们使用 lombok 的 CREATE TABLE cart_items ( cart_id INT, prod_id INT, quantity INT, UNIQUE KEY (cart_id, prod_id), FOREIGN KEY (cart_id) REFERENCES cart (cart_id), FOREIGN KEY (prod_id) REFERENCES product (prod_id) ); 那么你需要添加 @value

您可以在存在根 pom.xml 的同一位置创建一个名为 lombok.anyConstructor.addConstructorProperties=true 的文件,并将此行添加到文件中

https://stackoverflow.com/a/56022839/6700081