如何将Jackson映射器用于java.io.Serializable类型字段?

时间:2022-05-25 18:04:29

I have a situation where POJOs extend an abstract super class, which defines methods like getId() and setId() using java.io.Serializable type (code shown below). Whenever I am deserializing a JSON string to my concrete POJOs, I get following exception:

我有一种情况,其中POJO扩展了一个抽象超类,它使用java.io.Serializable类型定义了getId()和setId()等方法(如下所示)。每当我将JSON字符串反序列化为具体的POJO时,我都会遇到以下异常:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.io.Serializable, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
 at [Source: java.io.StringReader@6fd90825; line: 1, column: 2] (through reference chain: com.demo.jackson.AClass["id"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:716)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:140)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:525)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:99)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:242)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
    at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1270)
    at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:897)

The Java codes:

Java代码:

Abstract Super Class

抽象超级

public abstract class AbstractClass {
  protected abstract void setId(final Serializable id);
  protected abstract Serializable getId();
}

Implementation Class: AClass

实现类:AClass

public class AClass extends AbstractClass {
  private Long id;
  private String name;

  public Long getId() {
    return id;
  }
  public void setId(Serializable id) {
    this.id = (Long) id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Implementation Class: BClass

实现类:BClass

public class BClass extends AbstractClass {
  private String id;
  private String name;

  public String getId() {
    return id;
  }
    public void setId(Serializable id) {
    this.id = (String) id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

The testing class

测试类

public class JsonSerializerTest {
  public static void main(String[] args) throws Exception {
    final ObjectMapper objectMapper = new ObjectMapper();
    serialize(objectMapper);
  }

  private static void serialize(final ObjectMapper objectMapper) throws Exception {
    final String jsonString = "{\"id\":123,\"name\":\"AClass\"}";
    final ObjectReader objectReader = objectMapper.reader(AClass.class);
    final AClass a = objectReader.readValue(jsonString);
    System.out.println(a);
  }
}

Could someone provide some pointers?

有人可以提供一些指示吗?

~ NN

~NN

4 个解决方案

#1


3  

We had same situation, we have to use java.io.Serializable as ID in entities. With Serialization we have no problem, but in Deserialization we have the issue. In our application we use String in JOSN objects, so Stdandard StringDeserializer and it is working - in theory you can use it with any implementation of a Serializable. (Apache CXF and Jackson 2.4.1 is used):

我们有相同的情况,我们必须使用java.io.Serializable作为实体中的ID。使用序列化我们没有问题,但在反序列化中我们遇到了问题。在我们的应用程序中,我们在JOSN对象中使用String,因此Stdandard StringDeserializer正在运行 - 从理论上讲,您可以将它与Serializable的任何实现一起使用。 (使用Apache CXF和Jackson 2.4.1):

Initialize Jackson provider:

初始化Jackson提供者:

    // Jackson JSON Provider
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new IdentifiableDeserializableModule());

    JacksonJaxbJsonProvider jp = new JacksonJaxbJsonProvider(mapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
    jp.configure(SerializationFeature.INDENT_OUTPUT, indentJson);

And the module:

和模块:

import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.Serializable;

/**
 * Jackson module to deserialize java.io.Serializable class.
 */
public class IdentifiableDeserializableModule extends SimpleModule {
    public IdentifiableDeserializableModule() {
        addDeserializer(Serializable.class, new StringDeserializer());
    }
}

#2


1  

The solution that worked for us is shown below.

对我们有用的解决方案如下所示。

Abstract Class: Use Object instead of Serializable. I know Serializable suits better for IDs, but this issue was a kind of blocker for our app and we opted this solution.

抽象类:使用Object而不是Serializable。我知道Serializable更适合ID,但这个问题对我们的应用程序来说是一种阻止,我们选择了这个解决方案。

public abstract class AbstractClass {
  protected abstract void setId(final Object id);
  protected abstract Object getId();
}

Implementation: AClass

实施:AClass

public class AClass extends AbstractClass {
  private Long id;
  private String name;

  public Long getId() {
    return id;
  }
  public void setId(Object id) {
    // We need this inverted way to get a Long from a String, but we didn't have any other option!
    this.id = Long.valueOf(Objects.toString(id));
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Implementation: BClass

实施:BClass

public class BClass extends AbstractClass {
  private String id;
  private String name;

  public String getId() {
    return id;
  }
    public void setId(Object id) {
    this.id = Objects.toString(id);
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

I do not know if this is an elegant solution or not (compromising(?) Serializable for Objects), but please feel free to post a better one.

我不知道这是否是一个优雅的解决方案(妥协(?)Serializable for Objects),但请随意发布更好的解决方案。

Thanks.

谢谢。

~ NN

~NN

#3


1  

I recently ran into this situation. The solution I went with approaches the problem similar to the Answer posted above by Niranjan in using Object in lieu of Serializable. However, instead of using get/set methods with Object or attempting to create a separate custom de-serializer I leveraged the constructor to keep the field type as Serializable while allowing Jackson to provide an instance of Object.

我最近遇到了这种情况。我使用的解决方案解决了类似于Niranjan上面发布的使用Object代替Serializable的问题的问题。但是,我没有使用带有Object的get / set方法或尝试创建单独的自定义反序列化器,而是利用构造函数将字段类型保持为Serializable,同时允许Jackson提供Object的实例。

I used the @JsonCreator annotation on the constructor and then individual @JsonProperty annotations on the parameters (this isn't required in Java 8 using the Parameter Names Module making this an even cleaner solution).

我在构造函数上使用了@JsonCreator注释,然后在参数上使用了单独的@JsonProperty注释(这在Java 8中不需要使用参数名称模块,这使得它成为更清晰的解决方案)。

This answer also contains extensive information regarding @JsonCreator.

这个答案还包含有关@JsonCreator的广泛信息。

For the purpose of brevity in the answer the class defined below is immutable (all fields final and no setters).

为了简洁起见,下面定义的类是不可变的(所有字段都是final,没有setter)。

public class PersonLocation {
    private final Serializable id;
    private final String name;
    private final Location location;

    /**
     * Constructor leveraged by Jackson to de-serialize an incoming 
     * PersonLocation instance represented in JSON.
     *
     * @param id The instance identity.
     * @param name The name of the person.
     * @param location The location of the person.
     **/
    @JsonCreator
    public PersonLocation (
        @JsonProperty("id") Object id,
        @JsonProperty("name") String name,
        @JsonProperty("location") Location location)
    {
        //This safety check and cast should be a static utility method.
        if (!id instanceof Serializable) {
            throw new IllegalArgumentException("Id must be a serializable type.");
        }
        this.id = (Serializable)id;
        this.name = name;
        this.location = location;
    }

    public Serializable getId () {
        return id;
    }

    public String getName () {
        return name;
    }

    public Location getLocation () {
        return location;
    }
}

#4


0  

I don't know what options you have, but what about overloading the setId methods in the subclasses then make Jackson to ignore those which accept Serializable?

我不知道你有什么选择,但是在子类中重载setId方法然后让Jackson忽略那些接受Serializable的那些?

So, ClassA would look like this:

所以,ClassA看起来像这样:

public class AClass extends AbstractClass {
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }
    @JsonIgnore
    public void setId(Serializable id) {
        this.id = (Long) id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

This solution is not too elegant but it may be acceptable for you. If not then I suggest to check Jackson's custom serialization and/or polymorphic serialization support.

这个解决方案不是太优雅,但它可能是你可以接受的。如果没有,那么我建议检查Jackson的自定义序列化和/或多态序列化支持。

#1


3  

We had same situation, we have to use java.io.Serializable as ID in entities. With Serialization we have no problem, but in Deserialization we have the issue. In our application we use String in JOSN objects, so Stdandard StringDeserializer and it is working - in theory you can use it with any implementation of a Serializable. (Apache CXF and Jackson 2.4.1 is used):

我们有相同的情况,我们必须使用java.io.Serializable作为实体中的ID。使用序列化我们没有问题,但在反序列化中我们遇到了问题。在我们的应用程序中,我们在JOSN对象中使用String,因此Stdandard StringDeserializer正在运行 - 从理论上讲,您可以将它与Serializable的任何实现一起使用。 (使用Apache CXF和Jackson 2.4.1):

Initialize Jackson provider:

初始化Jackson提供者:

    // Jackson JSON Provider
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new IdentifiableDeserializableModule());

    JacksonJaxbJsonProvider jp = new JacksonJaxbJsonProvider(mapper, JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);
    jp.configure(SerializationFeature.INDENT_OUTPUT, indentJson);

And the module:

和模块:

import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;

import java.io.Serializable;

/**
 * Jackson module to deserialize java.io.Serializable class.
 */
public class IdentifiableDeserializableModule extends SimpleModule {
    public IdentifiableDeserializableModule() {
        addDeserializer(Serializable.class, new StringDeserializer());
    }
}

#2


1  

The solution that worked for us is shown below.

对我们有用的解决方案如下所示。

Abstract Class: Use Object instead of Serializable. I know Serializable suits better for IDs, but this issue was a kind of blocker for our app and we opted this solution.

抽象类:使用Object而不是Serializable。我知道Serializable更适合ID,但这个问题对我们的应用程序来说是一种阻止,我们选择了这个解决方案。

public abstract class AbstractClass {
  protected abstract void setId(final Object id);
  protected abstract Object getId();
}

Implementation: AClass

实施:AClass

public class AClass extends AbstractClass {
  private Long id;
  private String name;

  public Long getId() {
    return id;
  }
  public void setId(Object id) {
    // We need this inverted way to get a Long from a String, but we didn't have any other option!
    this.id = Long.valueOf(Objects.toString(id));
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

Implementation: BClass

实施:BClass

public class BClass extends AbstractClass {
  private String id;
  private String name;

  public String getId() {
    return id;
  }
    public void setId(Object id) {
    this.id = Objects.toString(id);
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

I do not know if this is an elegant solution or not (compromising(?) Serializable for Objects), but please feel free to post a better one.

我不知道这是否是一个优雅的解决方案(妥协(?)Serializable for Objects),但请随意发布更好的解决方案。

Thanks.

谢谢。

~ NN

~NN

#3


1  

I recently ran into this situation. The solution I went with approaches the problem similar to the Answer posted above by Niranjan in using Object in lieu of Serializable. However, instead of using get/set methods with Object or attempting to create a separate custom de-serializer I leveraged the constructor to keep the field type as Serializable while allowing Jackson to provide an instance of Object.

我最近遇到了这种情况。我使用的解决方案解决了类似于Niranjan上面发布的使用Object代替Serializable的问题的问题。但是,我没有使用带有Object的get / set方法或尝试创建单独的自定义反序列化器,而是利用构造函数将字段类型保持为Serializable,同时允许Jackson提供Object的实例。

I used the @JsonCreator annotation on the constructor and then individual @JsonProperty annotations on the parameters (this isn't required in Java 8 using the Parameter Names Module making this an even cleaner solution).

我在构造函数上使用了@JsonCreator注释,然后在参数上使用了单独的@JsonProperty注释(这在Java 8中不需要使用参数名称模块,这使得它成为更清晰的解决方案)。

This answer also contains extensive information regarding @JsonCreator.

这个答案还包含有关@JsonCreator的广泛信息。

For the purpose of brevity in the answer the class defined below is immutable (all fields final and no setters).

为了简洁起见,下面定义的类是不可变的(所有字段都是final,没有setter)。

public class PersonLocation {
    private final Serializable id;
    private final String name;
    private final Location location;

    /**
     * Constructor leveraged by Jackson to de-serialize an incoming 
     * PersonLocation instance represented in JSON.
     *
     * @param id The instance identity.
     * @param name The name of the person.
     * @param location The location of the person.
     **/
    @JsonCreator
    public PersonLocation (
        @JsonProperty("id") Object id,
        @JsonProperty("name") String name,
        @JsonProperty("location") Location location)
    {
        //This safety check and cast should be a static utility method.
        if (!id instanceof Serializable) {
            throw new IllegalArgumentException("Id must be a serializable type.");
        }
        this.id = (Serializable)id;
        this.name = name;
        this.location = location;
    }

    public Serializable getId () {
        return id;
    }

    public String getName () {
        return name;
    }

    public Location getLocation () {
        return location;
    }
}

#4


0  

I don't know what options you have, but what about overloading the setId methods in the subclasses then make Jackson to ignore those which accept Serializable?

我不知道你有什么选择,但是在子类中重载setId方法然后让Jackson忽略那些接受Serializable的那些?

So, ClassA would look like this:

所以,ClassA看起来像这样:

public class AClass extends AbstractClass {
    private Long id;
    private String name;

    public Long getId() {
        return id;
    }
    @JsonIgnore
    public void setId(Serializable id) {
        this.id = (Long) id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

This solution is not too elegant but it may be acceptable for you. If not then I suggest to check Jackson's custom serialization and/or polymorphic serialization support.

这个解决方案不是太优雅,但它可能是你可以接受的。如果没有,那么我建议检查Jackson的自定义序列化和/或多态序列化支持。