使 Jackson 反序列化为没有 setter 或构造函数的私有字段

Gre*_*tle 6 java json jackson

我设法将 Jackson 配置为序列化一个类,只需使用

objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
Run Code Online (Sandbox Code Playgroud)

但是我无法将这个非静态类的 JSON 字符串反序列化为没有任何参数化构造函数或设置器的对象。这是否可能 - 我认为它应该通过反思,但我不知道如何......

以下是需要通过的 2 个测试才能实现我所需要的:

public class ObjectMapperTest {

    private ObjectMapper mapper;

    @Before
    public void init() {
        mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    }

    @Test
    public void serialize() throws Exception {
        Payload payloadToSerialize = new Payload();
        payloadToSerialize.data = "testData";
        String serializedPayload = mapper.writeValueAsString(payloadToSerialize);
        assertThat(serializedPayload, is("{\"data\":\"testData\"}"));
        // --> OK
    }

    @Test
    public void deserialize() throws Exception {
        Payload deserializedPayload = mapper.readValue("{\"data\":\"testData\"}", Payload.class);
        assertThat(deserializedPayload.data, is("testData"));
        // com.fasterxml.jackson.databind.JsonMappingException:
        // No suitable constructor found for type [simple type, class ...ObjectMapperTest$Payload]:
        // can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
        // at [Source: {"data":"testData"}; line: 1, column: 2]
    }

    public class Payload {

        private String data;

        public Payload() {
            // empty constructor for Jackson
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

将 Payload 类设为静态将修复测试,但静态类对我来说不是一个选项,因为我没有使用项目中的内部有效负载类。任何想法如何通过对象映射器配置更改来修复它?

编辑 当我在 Spring MVC 应用程序中使用 Jackson 对象映射器在引擎盖下序列化/反序列化时,我需要一个仅更改或扩展对象映射器配置的解决方案。

Jun*_*lez 3

您可以编写自己的反序列化器来解析 JSON 并创建 的实例Payload,然后data使用反射设置值。示例:

@Before
public void init() {
    mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
    mapper.registerModule(
        new SimpleModule()
        .addDeserializer(Payload.class, new JsonDeserializer<Payload>() {
            @Override
            public Payload deserialize(JsonParser parser, DeserializationContext ctx)
                throws IOException, JsonProcessingException {
                JsonNode obj = parser.readValueAsTree(); // Read the JSON as a node

                Payload payload = new Payload();
                if (obj.isObject() && obj.has("data")) { // The node is an object and has a "data" field
                    try {
                        // Use reflection to set the value
                        Field dataField = payload.getClass().getDeclaredField("data");
                        dataField.setAccessible(true);
                        dataField.set(payload, obj.get("data").asText());
                    } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
                        throw new IOException("Reflection error", ex);
                    }
                }
                return payload;
            }
        }));
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果您想要更“通用”的东西,您可以尝试自己创建实例并更改所有字段的可访问性。然后您告诉 Jackson 使用 JSON 更新值。

public <T> T deserialize(final String json, final T instance) throws Exception {
    for (Field field : instance.getClass().getDeclaredFields()) {
        field.setAccessible(true);
    }
    mapper.readerForUpdating(instance).readValue(json);
    return instance;
}


@Test
public void deserializeUpdate() throws Exception {
    Payload deserializedPayload = deserialize("{\"data\":\"testData\"}", new Payload());
    assertThat(deserializedPayload.data, is("testData"));
}
Run Code Online (Sandbox Code Playgroud)

我在您的 Payload 类上测试了这一点,也许它不适用于更复杂的对象。

  • 我会给你+1,感谢你的努力和解决我的问题的好方法,但是当我使用Spring MVC时,它在幕后进行JSON到对象的转换,我不想在评估哪种类型后手动调用任何东西有效负载类进来。 (2认同)