【问题标题】:Serialize one class in two different ways with Jackson用杰克逊以两种不同的方式序列化一个类
【发布时间】:2012-08-27 11:59:18
【问题描述】:

在我们的一个项目中,我们使用 java webapp 与 MongoDB 实例对话。在数据库中,我们使用DBRefs 来跟踪一些对象关系。我们使用 jackson(使用 mongodb-jackson-mapper)对 POJO 对象进行(反)序列化。

但是,我们使用相同的 POJO 来(反)序列化到外部世界,我们的前端处理呈现 JSON。

现在,我们需要一种方法让外部世界的序列化包含来自 DBRef 的引用对象(以便 UI 可以呈现完整的对象),而我们显然希望将 DBRef 写入数据库,而不是整个对象。

现在我写了一些未经测试的静态嵌套类代码:

public static class FooReference {
    public DBRef<Foo> foo;

    // FIXME how to ensure that this doesn't go into the database?
    public Foo getFoo() {
        return foo.fetch();
    }
}

理想情况下,我想要一种对此进行注释的方法,以便我可以(反)序列化它,无论是否有 getFoo() 结果,可能取决于某些配置对象。这可能吗?你有没有更好的方法来做这件事?

【问题讨论】:

  • 我该怎么做以及如何解决这个问题?这是我的代码,所以我可以做任何事情,但您的评论不清楚,Google 也没有提供帮助,抱歉。
  • "transient" 是一个 java 关键字,表示不应序列化/持久化字段 - java-samples.com/showtutorial.php?tutorialid=331
  • 呃,这是一个字段修饰符。这是一种方法。
  • mongodb 是否有方法的 Transient 或 NonPersistant 注解?
  • Mongo 本身有驱动程序(处理普通的类似地图的对象),这些驱动程序已经以各种不同的方式扩展/替换/包装以提供 POJO 支持。事实证明,Morphia 有一个 Transient 和一个 NotStored 注释,但我使用的是 mongo-jackson-mapper。更多的谷歌搜索表明 Jackson 支持用于序列化的视图,而 mongo-jackson-mapper 允许您指定要使用的视图,所以这似乎可行。谢谢指点!

标签: java mongodb jackson pojo mongo-jackson-mapper


【解决方案1】:

通过查看选项,您似乎可以注释属性以仅在给定的View 传递给用于序列化的ObjectMapper 时显示。因此,您可以编辑该类:

public static class FooReference {
    public DBRef<Foo> foo;

    @JsonView(Views.WebView.class)
    public Foo getFoo() {
        return foo.fetch();
    }
}

并提供:

class Views {
    static class WebView { }
}

然后在使用正确视图创建配置后进行序列化:

SerializationConfig conf = objectMapper.getSerializationConfig().withView(Views.WebView.class);
objectMapper.setSerializationConfig(conf);

然后将其序列化。使用 MongoDB 包装器进行序列化时未指定视图将意味着该方法将被忽略。没有 JsonView 注解的属性默认是序列化的,你可以通过指定来改变这个行为:

objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);

更多信息请见on the Jackson Wiki

事实证明,还有其他选择:Jackson MixIns 可以让您在不修改类本身的情况下覆盖(反)类的部分序列化行为,并且从 Jackson 2.0(最新版本)开始) 还有filters

【讨论】:

    【解决方案2】:

    使用自定义 JSONSerializer 并在 serialize 方法中应用您的逻辑:

    public static class FooReference {
        public DBRef<Foo> foo;
    
        @JsonSerialize(using = CustomSerializer.class)
        public Foo getFoo() {
            return foo.fetch();
        }
    }
    
    public class CustomSerializer extends JsonSerializer<Object> {
       public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
           throws IOException, JsonProcessingException {
         // jgen.writeObjectField ...
       }
    }
    

    【讨论】:

    • 这不会无条件地呼叫getFoo()吗?那将是次优的,因为它意味着数据库请求。此外,据我所知,序列化程序将使用类 ref 构造,因此它没有可以推断是否序列化对象的上下文。所以我需要依赖于我在序列化/反序列化之前/之后旋转的同一个线程中存在的某种静态标志?这似乎很脆弱。
    • 是的,我误读了您的问题。但是,您可以返回一个包装器,而不是返回foo.fetch(),它在serialize 中将确定您是否需要发出数据库请求或序列化该字段。
    • 就我提出的第一点而言,这听起来不错。关于第二个:理想情况下,我不希望模型/POJO 中有关于模型外部发生的任何数据(即“我如何被序列化”)。也就是说,当我调用(反)序列化它时,我更愿意“选择”两种序列化方法之一。这是否有意义,是否可以使用您描述的方法? (在任何情况下都支持指出 JsonSerialize,但还不确定它是否可以解决这个问题)
    • 我跟着你,用这种方法是可以的,但它确实会以某种方式将序列化机制与模型耦合。 在 Jackson 中知道的唯一允许您执行此操作的机制实际上是 JSONSerializer,所以恐怕您必须将自定义逻辑放在那里。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    相关资源
    最近更新 更多