【问题标题】:Is it possible to have a Jackson custom deserializer with non-default constructor?是否可以使用带有非默认构造函数的 Jackson 自定义反序列化器?
【发布时间】:2011-12-08 12:49:03
【问题描述】:

假设我有以下课程:

public class Person {

    String name;
    Set<Department> departments;

}

public class Department {

    String code;
    String name;

}

所以我想编写一个自定义的部门反序列化器,以便注释 Person 类中的 deparments 字段以使用它。因为此自定义反序列化器将仅用于反序列化 Person 对象内的 Department 对象。问题是我的自定义部门反序列化器需要有一个必须在反序列化器的构造函数中传递的 DepartmentRepository。我怎样才能做到这一点?这可能吗?我不想在对象映射器中注册反序列化器,因为它只能在 Person 类的 deparatments 字段被反序列化时使用。

更新:我需要的是,除了使用参数 contentUsing = MyCustomDepartmentDeserializer.class 使用 JsonDeserialize 注释对部门字段进行注释之外,这是一种告诉杰克逊,当它创建 MyCustomDepartmentDeserializer 对象时,它必须通过调用接收 DepartmentRepository 的构造函数来完成。反序列化器可能是这样的:

public class MyCustomDepartmentDeserializer extends JsonDeserializer<Department> {

    private final DepartmentRepository departmentRepository;

    public MyCustomDepartmentDeserializer(DepartmentRepository departmentRepository) {
        this.departmentRepository = departmentRepository;
    }

    @Override
    public Department deserialize(JsonParser jp, DeserializationContext ctxt)
    throws IOException, JsonProcessingException {
        //IMPLEMENTATION!
    }

}

【问题讨论】:

    标签: java deserialization jackson


    【解决方案1】:

    首先要做的事情:指定反序列化器用于您可以使用的数组内容

    @JsonDeserialize(contentUsing=MyDeserializer.class)
    Set<Department> departments;
    

    指定反序列化器用于相关集合的内容。

    关于使用非默认构造函数的能力,@JsonCreator 允许这样做。 但是要传递上下文对象,您需要 Jackson 1.9 可能是您的朋友(请参阅“Jackson 1.9 overview”),它允许在 JSON 之外“注入”对象。 然后,您可以混合和匹配可注入值和 JSON 属性,例如:

    public class POJO {
      @JsonCreator // can also be used for static factory methods
      public POJO(@JacksonInject DepartmentRepository repo, @JsonProperty("value") int value) {
          ....
      }
    }
    

    这可能足以满足您的要求。

    【讨论】:

    • 我刚刚尝试在反序列化器构造函数中使用@JacksonInject,但它不起作用。它似乎只适用于将要反序列化的类。
    • 正确,它不适用于反序列化器,因为 Jackson 从不实例化反序列化器(注册是使用反序列化器实例,而不是类)。但这是一个问题吗?您可以在反序列化器类的构造函数/工厂方法中注入它,您可以根据需要在哪里使用它?或者只是被反序列化器用来查找传递给构造函数的其他东西?
    • 据我所知,当您不在对象映射器中注册解串器时,杰克逊会实例化它们。当您只使用 @JsonDeserializer(contentUsing=MyCustomDeserializer.class) 时,Jackson 会完成实例化。
    • 啊,是的,忘记了那部分,你是对的。有趣——那里可能有注入的功能请求。然而,一个问题是反序列化器被实例化一次,被缓存。甚至是上下文相关的。
    • 嗯,这很奇怪。因为我正在使用 Jersey + Google Guice 开发一个带有 REST API 的 Web 服务,并且每次遇到一个必须反序列化一个部门的方法时,都会创建一个新的反序列化器。我目前正在通过使用提供存储库的静态帮助程序类来解决此问题。这个存储库是使用 guice 注入的。但这不是最好的解决方案。另一方面,我需要在每个请求上创建反序列化程序,因为存储库依赖于实体管理器,并且当您使用 guice 时,会为每个请求创建一个新的 EM。
    【解决方案2】:

    您可以通过将其注册为ObjectMapper 的模块来添加具有非默认构造函数的自定义序列化器/反序列化器。

    SimpleModule simpleModule = new SimpleModule();
    JsonDeserializer<MyObject> customDeserializer = new CustomDeserializer("Blah");
    testModule.addDeserializer(MyObject.class, customDeserializer);
    mapper.registerModule(simpleModule);
    

    如果MyObject 类中有注释,您还应该删除它。

    【讨论】:

      【解决方案3】:

      这是我刚刚写的一个反序列化器。注意使用非默认构造函数。

      public class SparseStringArrayVectorDeserializer extends JsonDeserializer<SparseStringArrayVector> {
      
      @Override
      public SparseStringArrayVector deserialize(JsonParser jp, DeserializationContext ctxt)
          throws IOException, JsonProcessingException {
      
          /* This isn't the most efficient way to do this, since we're building a tree of nodes that we will discard.
           * However, we need to change the order around, so something like this is hard to avoid.
           */
          JsonNode tree = jp.readValueAsTree();
          int tokenCount = tree.size();
          int[] indexes = new int[tokenCount];
          String[][] strings = new String[tokenCount][];
          Iterator<Entry<String, JsonNode>> fieldNameIt = tree.getFields();
          int slot = 0;
          while (fieldNameIt.hasNext()) {
              Entry<String, JsonNode> entry = fieldNameIt.next();
              int index = Integer.parseInt(entry.getKey());
              indexes[slot] = index;
              String[] thisTokenStrings = new String[entry.getValue().size()];
              for (int x = 0; x < thisTokenStrings.length; x++) {
                  thisTokenStrings[x] = entry.getValue().get(x).getTextValue();
              }
              strings[slot] = thisTokenStrings;
              slot++;
          }
          return new SparseStringArrayVector(indexes, strings);
      }
      }
      

      与以下一起使用。请注意,在创建反序列化程序并将其添加到模块时,您可以使用任何您喜欢的构造函数模式。

       ObjectMapper mapper = new ObjectMapper();
          SimpleModule module = new SimpleModule("ResultAccess", new Version(7, 4, 0, null));
          module.addDeserializer(SparseStringArrayVector.class, new SparseStringArrayVectorDeserializer());
          module.addDeserializer(AbstractResultAccess.class, new ProxyAbstractResultAccessDeserializer());
          mapper.registerModule(module);
      

      【讨论】:

      • 非默认构造函数是针对反序列化的,而不是针对被反序列化的类
      • "tree.getFields()" 应该是 "tree.fields()",据我所知。
      【解决方案4】:

      不,一开始,您可以不指定自定义反序列化器; Jackson 可以检测您的嵌套字段并正确映射它们,只有当所有模型类都实现 Serializable

      因此,将implements Serializable 添加到DepartmentPerson,您将看到Jackson 开箱即用。

      【讨论】:

        【解决方案5】:

        就在我的脑海中,我很确定您可以使用 Jackson 中的注释来确定您想要公开的属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-07-15
          • 1970-01-01
          • 2021-02-06
          • 2014-09-07
          • 2018-05-15
          • 1970-01-01
          • 2015-04-25
          • 2018-08-18
          相关资源
          最近更新 更多