Jackson JSON 动态键值绑定到 Java Bean
Jackson JSON Dynamic Key Value Binding To Java Bean
我正在尝试使用 RESTTemplate api 为以下 JSON 格式编写 POJO class,但我无法使用 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"}
无论如何你都需要编写一个序列化器,这是一个通用的序列化器,它将序列化所有映射到 "HOBBIES[0]
这种形式。
@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需要这个功能,加一个serializer annotation很烦人,所以,你可以把这个serializer写成修饰符。
@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"}
我正在尝试使用 RESTTemplate api 为以下 JSON 格式编写 POJO class,但我无法使用 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"}
无论如何你都需要编写一个序列化器,这是一个通用的序列化器,它将序列化所有映射到 "HOBBIES[0]
这种形式。
@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需要这个功能,加一个serializer annotation很烦人,所以,你可以把这个serializer写成修饰符。
@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"}