【问题标题】:Jackson JSON Dynamic Key Value Binding To Java BeanJackson JSON 动态键值绑定到 Java Bean
【发布时间】:2017-04-26 07:41:12
【问题描述】:

我正在尝试使用 RESTTemplate api 为以下 JSON 格式编写 POJO 类,但我未能使用 Map 解析所需的 json 格式。 我应该使用什么属性类型来获得低于 json 格式?请给我建议。

@JsonProperty("ID")
private String id = null;
@JsonProperty("NAME")
private String name = null;
@JsonProperty("AGE")
private int age = 0;
@JsonProperty("HOBBIES")
private Map<String, String> hobbies = null;

生成的 JSON 格式:

{"NAME":"Shas","ID":"1","AGE":29,"HOBBIES":{"HOBBIES[1]":"Chess","HOBBIES[0]":"Cricket"}}

预期格式:

{"NAME":"Shas","ID":"1","AGE":29,"HOBBIES[0]":"Cricket", "HOBBIES[1]":"Chess"}

【问题讨论】:

标签: java json rest jackson resttemplate


【解决方案1】:

无论如何你都需要写一个序列化器,这是一个通用序列化器,会将所有提交到"HOBBIES[0]这个表单的Map序列化。

@Test
public void test() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    String json = mapper.writeValueAsString(new POJO().setHobbies(ImmutableMap.of("0", "Cricket", "1", "Chess")));
    System.out.println(json);
}

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MapAsField {

}

@Data
@Accessors(chain = true)
@JsonSerialize(using = CustomJsonSerializer.class)
public static class POJO {

    @JsonProperty("ID")
    private String id = "abc";
    @JsonProperty("NAME")
    private String name = "wener";
    @JsonProperty("AGE")
    private int age = 0;
    @JsonProperty("HOBBIES")
    @MapAsField
    private Map<String, String> hobbies = null;
    private Map<String, String> fav = ImmutableMap.of("A", "Yes", "B", "No");
}

static class CustomJsonSerializer extends JsonSerializer<Object> {

    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        JavaType javaType = provider.constructType(Object.class);
        BeanDescription beanDesc = provider.getConfig().introspect(javaType);
        ListIterator<BeanPropertyDefinition> itor = beanDesc.findProperties()
            .listIterator();

        // Remove map field
        ArrayList<BeanPropertyDefinition> list = new ArrayList<>();
        while (itor.hasNext()) {
            BeanPropertyDefinition n = itor.next();

            if (n.getField().getAnnotated().getAnnotation(MapAsField.class) != null && // Only handle this
                Map.class.isAssignableFrom(n.getField().getRawType())) {
                itor.remove();
                list.add(n);
            }
        }

        JsonSerializer<Object> serializer = BeanSerializerFactory
            .instance
            .findBeanSerializer(provider, javaType, beanDesc);
        serializer.unwrappingSerializer(null).serialize(value, gen, provider);

        // Handle all map field
        for (BeanPropertyDefinition d : list) {
            try {
                Field field = d.getField().getAnnotated();
                field.setAccessible(true);
                Map<?, ?> v = (Map<?, ?>) field.get(value);
                if (v != null && !v.isEmpty()) {
                    for (Map.Entry o : v.entrySet()) {
                        gen.writeStringField(
                            String.format("%s[%s]", d.getName(), o.getKey().toString()),
                            o.getValue().toString()
                        );
                    }
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        gen.writeEndObject();
    }
}

输出

{"fav":{"A":"Yes","B":"No"},"ID":"abc","NAME":"wener","AGE":0,"HOBBIES[0]":"Cricket","HOBBIES[1]":"Chess"}

更新

如果你有很多bean需要这个功能,添加一个序列化器注解很烦人,所以,你可以把这个序列化器写成修饰符。

@Test
public void test2() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new SimpleModule().setSerializerModifier(new MapAsFieldModifier()));
    System.out.println(mapper.writeValueAsString(new POJO2()));
}
@Data
@Accessors(chain = true)
public static class POJO2 {

    @JsonProperty("ID")
    private String id = "abc";
    @JsonProperty("NAME")
    private String name = "wener";
    @JsonProperty("AGE")
    private int age = 0;
    @JsonProperty("HOBBIES")
    @MapAsField
    private Map<String, String> hobbies = ImmutableMap.of("0", "Cricket", "1", "Chess");
    private Map<String, String> fav = ImmutableMap.of("A", "Yes", "B", "No");
}

public class MapAsFieldModifier extends BeanSerializerModifier {

    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
        JsonSerializer<?> serializer) {
        ListIterator<BeanPropertyDefinition> itor = beanDesc.findProperties().listIterator();
        // Remove map field
        ArrayList<BeanPropertyDefinition> list = new ArrayList<>();
        while (itor.hasNext()) {
            BeanPropertyDefinition n = itor.next();

            if (n.getField().getAnnotated().getAnnotation(MapAsField.class) != null && // Only handle this
                Map.class.isAssignableFrom(n.getField().getRawType())) {
                itor.remove();
                list.add(n);
            }
        }

        // We should handle this
        if (!list.isEmpty()) {

            return new JsonSerializer<Object>() {
                @Override
                public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers)
                    throws IOException, JsonProcessingException {
                    gen.writeStartObject();

                    JavaType javaType = serializers.constructType(value.getClass());
                    JsonSerializer<Object> ser = BeanSerializerFactory
                        .instance
                        .findBeanSerializer(serializers, javaType, beanDesc);

                    ser.unwrappingSerializer(null).serialize(value, gen, serializers);

                    // Handle all map field
                    for (BeanPropertyDefinition d : list) {
                        try {
                            Field field = d.getField().getAnnotated();
                            field.setAccessible(true);
                            Map<?, ?> v = (Map<?, ?>) field.get(value);
                            if (v != null && !v.isEmpty()) {
                                for (Map.Entry o : v.entrySet()) {
                                    gen.writeStringField(
                                        String.format("%s[%s]", d.getName(), o.getKey().toString()),
                                        o.getValue().toString()
                                    );
                                }
                            }
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }

                    gen.writeEndObject();
                }
            };
        }

        return serializer;
    }
}

输出

{"fav":{"A":"Yes","B":"No"},"ID":"abc","NAME":"wener","AGE":0,"HOBBIES[0]":"Cricket","HOBBIES[1]":"Chess"}

【讨论】:

  • @wener。感谢它的工作。如何 private Map hobbies = null;正在链接到 CustomJsonSerializer 类。为什么我们需要班级级别的 JsonSerialize 而不是特定的兴趣爱好。
  • 我需要为那个特定的爱好地图而不是那个 bean 中的其他地图进行序列化。
  • 字段的序列化器只能控制字段值的序列化,{..."HOBBIES":&lt;CAN ONLY CONTROL THIS&gt;},你可以添加一个简单的注解来标记字段,序列化器可以检测到。
  • @wener 已注明并感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-20
  • 2018-01-29
  • 2019-06-26
  • 2022-08-18
  • 2010-10-13
  • 1970-01-01
相关资源
最近更新 更多