【问题标题】:How can I use Jackson mapper for java.io.Serializable type fields?如何将 Jackson 映射器用于 java.io.Serializable 类型字段?
【发布时间】:2014-10-10 18:51:11
【问题描述】:

我有一种情况,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)

Java 代码:

抽象超类

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

实现类: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;
  }
}

实现类: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;
  }
}

测试类

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

有人能指点一下吗?

~NN

【问题讨论】:

  • 我不认为 serializable 是一种类型,它是一个接口。您的抽象 getid 也是可序列化类型,但您的具体方法是长类型
  • 是的,我知道这一点,这是设计使然。我们的框架团队提供此类接口和抽象类,而实现由应用开发团队提供。

标签: java json


【解决方案1】:

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

初始化 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);

还有模块:

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】:

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

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

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

    实现: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;
      }
    }
    

    实现: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;
      }
    }
    

    我不知道这是否是一个优雅的解决方案(妥协(?)对象可序列化),但请随时发布更好的解决方案。

    谢谢。

    ~NN

    【讨论】:

      【解决方案3】:

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

      我在构造函数上使用了@JsonCreator 注释,然后在参数上使用了单独的@JsonProperty 注释(这在Java 8 中不需要,使用Parameter Names Module 使其成为更清洁的解决方案)。

      This answer 还包含有关@JsonCreator 的大量信息。

      为了简洁起见,下面定义的类是不可变的(所有字段都为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】:

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

        所以,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;
            }
        }
        

        此解决方案不太优雅,但您可能可以接受。如果没有,那么我建议查看 Jackson 的 custom serialization 和/或 polymorphic serialization 支持。

        【讨论】:

        • 谢谢你,但恐怕这行不通,因为@JsonIgnore 是忽略该属性。因此,使用这种方法,我的 ID 值不会在反序列化期间填充。让我看一下您提供的链接。我见过这些自定义和多态序列化,但不知何故我没有找到与我的问题类似的好例子。
        • 好的,但是看看这个关于 JsonIgnore 重载方法的问题:stackoverflow.com/questions/6346018/…
        猜你喜欢
        • 2018-01-08
        • 2022-08-03
        • 1970-01-01
        • 1970-01-01
        • 2013-02-24
        • 1970-01-01
        • 2011-10-06
        • 2014-08-23
        • 1970-01-01
        相关资源
        最近更新 更多