杰克逊:如何防止现场序列化(同时保持反序列化)

时间:2014-09-04 21:08:17

标签: java json serialization jackson

我想进一步了解this question的内容,我现在已经漫游了一段时间而没有找到任何东西。

基本上,我尝试做的是在反序列化期间通过杰克逊内部反射算法正确地实现属性,但在序列化时具有相同的属性序列化。

我知道@JsonIgnore@JsonIgnoreProperties,但显然我似乎无法正确使用它们:当我向Jackson提供适当的属性地图时,我的属性正确反序列化,但它也会出现在序列化结果中,(使用@JsonIgnore时)它不是序列化的(这是想要的),但也没有反序列化(不想要)。

示例:

public class Foo {

    /* This is the property I want to be instanciated by Jackson upon deserialization
     * but not serialized upon serialization
     */
    private final Object bar = null;

    public Object getBar() {
        return bar;
    }
}

更糟糕的是,正如您所看到的,该属性是最终的(这就是为什么我热衷于通过反序列化在Foo实例化时使用Jackson反射能力)。我已经阅读了关于以不同方式注释setter和getter的潜在解决方案,但我想尽可能保持此属性的最终结果。 如果不可能,我会选择非最终财产。

我希望得到的答案不是建议自定义序列化器/解串器,我的代码库目前是免费的,如果解决方案可能影响很小,那将是完美的。再说一遍,我不是杰克逊的专家,所以如果我提出的问题不可能,我显然会接受其他答案。

我还阅读了this thread on github,但目前尚未实施任何建议的实施方式。

由于


编辑:让事情更清楚

public class Foo {

    private final String bar = null;

    public String getBar() {
        return bar;
    }

    @Override
    public String toString() {
        return bar;
    }
}


public void testMethod() throws IOException {
        String json = "{\"bar\":\"Value\"}";
        ObjectMapper mapper = new ObjectMapper();
        Foo foo = mapper.readValue(json, Foo.class);
        System.out.println(foo); // should have a bar property set to "Value"
        System.out.println(mapper.writeValueAsString(foo)); // should return an empty JSON object
}

2 个答案:

答案 0 :(得分:2)

我不确定它是否是优雅的解决方案,但您可以使用MixIn功能。您必须创建如下所示的新界面:

interface FooMixIn {

    @JsonIgnore
    String getBar();
}

假设您的POJO看起来像这样:

class Foo {

    private final String bar = null;

    public String getBar() {
        return bar;
    }

    @Override
    public String toString() {
        return bar;
    }
}

现在您必须告诉Jackson您要忽略此属性:

String json = "{\"bar\":\"Value\"}";
System.out.println(json);
ObjectMapper deserializeMapper = new ObjectMapper();
deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println(deserializeMapper.readValue(json, Foo.class));

以上示例打印:

{"bar":"Value"}
null

程序上方没有deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class);行打印:

{"bar":"Value"}
Value

编辑1


如果你想获得像你所示的结果,你必须创建两个ObjectMapper并自定义它们。见下面的例子:

String json = "{\"bar\":\"Value\"}";
ObjectMapper deserializerMapper = new ObjectMapper();
Foo foo = deserializerMapper.readValue(json, Foo.class);
System.out.println("Foo object: " + foo);

ObjectMapper serializerMapper = new ObjectMapper();
serializerMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
serializerMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println("JSON: " + serializerMapper.writeValueAsString(foo));

对于序列化,您必须使用一个实例,并且为了反序列化,您必须使用另一个实例。

答案 1 :(得分:0)

Jackson 2.6开始,可以将属性标记为只读或只写。它比攻击两个访问器(非最终字段)上的注释更简单,并将所有信息保存在一个地方。重要的是要注意杰克逊默认将最终字段 视为可写。

但是,最终字段允许反序列化是不够的,因为你不能在该字段上设置一个setter:它需要通过构造函数设置,直接或使用构建器或其他可以反序列化的类型杰克逊。使用带有属性作为参数的构造函数时,需要使用@JsonProperty指定哪个参数对应于哪个属性:

public class Foo {
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private final String bar;

    public Foo(@JsonProperty("bar") String bar) {
        this.bar = bar;
    }

    public String getBar() {
        return prop;
    }
}