如何在使用Jackson反序列化Json时对所有值应用转换

时间:2016-01-16 23:30:01

标签: java jackson

我想用Jackson解析JSON文档,并在所有节点上应用一些转换。例如,假设我希望反序列化后所有值都为大写。

实际用例有点复杂:

  • 转换更复杂,变换器类需要注入一些配置,我希望它是一个可配置的实例
  • 转换必须在所有属性上进行,我希望不能在反序列化的每个类的每个属性上添加注释。

杰克逊有足够的配置选项/挂钩,所以我很确定这是可能的,我找不到自己的方式。

下面的测试显示了我想要实现的目标:

public class JsonValueFilterTest {

    private ObjectMapper mapper;

    @Before
    public void setupObjectMapper() {
        mapper = new ObjectMapper();
        // TODO: configure mapper to upper case all values
    }

    @Test
    public void printJson() throws IOException {
        Entity myEntity = new Entity("myName");
        mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
    }

    @Test
    public void valuesAreUpperCasedWhenLoaded() throws IOException {
        Entity myEntity = mapper.readValue("{\"name\":\"myName\"}", Entity.class);
        assertThat(myEntity.getName()).isEqualTo("MYNAME"); // fails
    }

    public static class Entity {
        private final String name;

        @JsonCreator
        public Entity(@JsonProperty("name") String name) { this.name = name; }

        public String getName() { return name; }

        @Override
        public String toString() { return "name='" + name + "'"; }
    }
}

2 个答案:

答案 0 :(得分:4)

对于这种简单的情况,您可以使用转换器来实现自定义反序列化器。我不知道为什么,但它不是在创建者构造函数上工作。所以你必须使用非最终字段。

public class JsonValueFilterTest {

    private ObjectMapper mapper;

    @BeforeTest
    public void setupObjectMapper() {
        mapper = new ObjectMapper();
    }

    @Test
    public void printJson() throws IOException {
        Entity myEntity = new Entity("myName");
        mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
    }

    @Test
    public void valuesAreUpperCasedWhenLoaded() throws IOException {
        Entity myEntity = mapper.readValue("{\"name\":\"myName\"}", Entity.class);
        Assert.assertEquals(myEntity.getName(), "MYNAME"); // fails
    }

    public static class UpCaseConverter extends StdConverter<String, String> {
        public String convert(String value) {
            return value==null ? null : value.toUpperCase();
        }
    }

    public static class Entity {

        private String name;

        public Entity() {}

        public Entity(String name) {
            this.name = name;
        }

        @JsonDeserialize(converter = UpCaseConverter.class)
        public void setName(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "name='" + name + "'";
        }
    }
}

答案 1 :(得分:1)

我的最终解决方案(感谢Alban):

  1. 使用自定义JsonNodeFactory配置ObjectMapper,该自定义JsonNodeFactory转换所有文本节点
  2. 将json反序列化为JsonNode(这将应用转换)
  3. JsonNode转换为我的自定义类

    public class JsonValueFilterTest {

    private ObjectMapper mapper;
    
    @Before
    public void setupObjectMapper() {
        mapper = new ObjectMapper();
        mapper.setNodeFactory(new JsonNodeFactory() {
            @Override
            public TextNode textNode(String text) {
                return super.textNode(text.toUpperCase());
            }
        });
    }
    
    @Test
    public void printJson() throws IOException {
        Entity myEntity = new Entity("myName");
        mapper.writeValue(System.out, myEntity); // prints: {"name":"myName"}
    }
    
    @Test
    public void valuesAreUpperCasedWhenLoaded() throws IOException {
        JsonNode jsonNode = mapper.readTree("{\"name\":\"myName\"}");
        Entity myEntity = mapper.treeToValue(jsonNode, Entity.class);
        assertThat(myEntity.getName()).isEqualTo("MYNAME");
    }
    
    public static class Entity {
        private final String name;
    
        @JsonCreator
        public Entity(@JsonProperty("name") String name) { this.name = name; }
    
        public String getName() { return name; }
    
        @Override
        public String toString() { return "name='" + name + "'"; }
    }
    

    }