2012-07-23 68 views
3

我对Jackson库(版本1.9)很陌生。我仅在几个星期后才使用它,并且我发现它非常灵活并且节省时间,因为它是关于在Java中对对象进行序列化和反序列化的。使用Jackson的不可变类反序列化JSON平面对象

但是,我遇到了麻烦,但是,将“扁平”JSON反序列化为另一个类的组合时,两者都意味着不可变。

我的情况是相当多的以下内容:

class Foo { 

    private final String var1; 

    Foo(String var1) { 
      this.var1 = var1; 
    } 
    // getters omitted 
} 

class A { 
    private final Foo foo; 
    private final String var2; 

    A(/* @JsonUnwrapped doesn't work here */ Foo foo, String var2) { 
      this.foo = foo; 
      this.var2 = var2; 
    } 

    @JsonUnwrapped 
    Foo getFoo() { 
     return foo; 
    } 

    String getVar2() { 
     return var2; 
    } 
} 

class B extends Foo { 
    private final String var2; 

    B(String var1, String var2) { 
      super(var1); 
      this.var2 = var2; 
    } 
    // getters omitted 
} 

而且JSON反序列化是这样的:

{ "var1" : "some_value", "var2" : "some_other_value" } 

的问题是:是否有一个基于注释的方式(这样,而不需要使用自定义的反序列化器)告诉Jackson将给定的JSON组合成一个'A'实例? 我已经尝试在类'A'构造函数中为Foo参数使用@JsonUnwrapped属性,但它在多参数构造函数中不受支持,因为它需要JsonProperty才能工作(这没有意义,因为实际上这些项目没有单独的财产)。相反,序列化可以完美地使用这种模式。

它还将与非不变类使用单独制定者的工作,但我想知道是否有一种方法只用构造函数(或建造者做同样的,这将使意义,因为在实际情况远不止这个例子中的那个)。

非常相同的方法显然适用于继承自'Foo'的类'B'。

在此先感谢。

+0

由于某些原因,你使用1.9而不是2+吗? – 2012-07-23 21:49:41

+0

我已经开始使用与当前为1.9的google-http-java-client捆绑在一起的Jackson版本。4,然后我继续使用Jackson本身,而不是Google库的更高级可插入JSON解析器(它对注释的支持少得多)。 – fast3r 2012-07-24 08:32:32

+0

无论如何,没有任何东西可以阻止我使用2.0版本,除了任何(据称)已知的问题。更重要的是,我对尝试使用Afterburner模块感到非常好奇,后者应该通过减少反射开销来加速JSON数据绑定[链接](http://www.cowtowncoder.com/blog/archives/2012/04 /entry_470.html) – fast3r 2012-07-24 08:38:35

回答

4

请注意,Jackson的反序列化处理并不一定尊重final字段的不变性。所以,一个简单的方法就是为Jackson提供无参数(私有)构造函数。

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; 
import com.fasterxml.jackson.annotation.JsonUnwrapped; 
import com.fasterxml.jackson.annotation.PropertyAccessor; 
import com.fasterxml.jackson.databind.ObjectMapper; 

public class JacksonFoo 
{ 
    public static void main(String[] args) throws Exception 
    { 
    // {"var1":"some_value", "var2":"some_other_value"} 
    String jsonInput = "{\"var1\":\"some_value\", \"var2\":\"some_other_value\"}"; 

    ObjectMapper mapper = new ObjectMapper().setVisibility(PropertyAccessor.FIELD, Visibility.ANY); 

    A a = new A(new Foo("some_value"), "some_other_value"); 
    System.out.println(mapper.writeValueAsString(a)); 
    // output: {"var1":"some_value","var2":"some_other_value"} 

    A aCopy = mapper.readValue(jsonInput, A.class); 
    System.out.println(mapper.writeValueAsString(aCopy)); 
    // output: {"var1":"some_value","var2":"some_other_value"} 
    } 
} 

class Foo 
{ 
    private final String var1; 

    Foo(String var1) {this.var1 = var1;} 

    private Foo() {this.var1 = null;} 
} 

class A 
{ 
    @JsonUnwrapped 
    private final Foo foo; 
    private final String var2; 

    A(Foo foo, String var2) 
    { 
    this.foo = foo; 
    this.var2 = var2; 
    } 

    private A() 
    { 
    this.foo = null; 
    this.var2 = null; 
    } 
} 

如果你真的不想提供这种(额外)构造函数,那么这将是很好,如果类似的解决方案可以使用@JsonCreator来设计的,但我没能得到这样的东西的工作。因此,我建议您在https://github.com/FasterXML/jackson-core/issues处记录增强请求,可能更好地支持使用@JsonUnwrapped@JsonProperty注释@JsonCreator参数。

+0

您的解决方案可能是唯一适用于这种情况的解决方案。然而,我使用最终字段来强制不变性,因此线程安全而不需要使用易变的字段,并且我假设Jackson在提供有效的注释构造函数时不使用反射来设置字段。使用反射可能会导致该类可能存在内存可见性问题,这就是我试图避免使用不可变类的原因。 我认为除了向杰克逊询问一个增强请求之外没有其他解决方案:非常感谢您的答案! – fast3r 2012-07-23 22:41:25

+0

你是对的,如果使用'@ JsonCreator'来传递属性值,则不会使用setter。 – StaxMan 2012-07-24 17:04:18

0

不幸的是,某些功能的组合可能无法正确实现;这可能是其中的一种(我不是100%确定的:随意为Jackson github问题或Jira提交Bug/RFE)。这是因为@JsonUnwrapped@JsonCreator都需要潜在的数据重新排序;还因为创建实际实例的顺序使事情变得复杂。 因此,尽管在概念上这应该是可能的,但可能存在实施困难。至于杰克逊2.0:我肯定会尝试1.9以上,因为@JsonUnwrapped处理的某些部分已经改进;并在那里添加任何修复/改进。 1.9分支将尽可能地向后移动错误修正,但不会添加新功能。