Java 如何使用Jackson将列表内容序列化为平面JSON对象?
鉴于以下情况:Java 如何使用Jackson将列表内容序列化为平面JSON对象?,java,json,serialization,jackson,nested,Java,Json,Serialization,Jackson,Nested,鉴于以下情况: public class City { private String title; private List<Person> people; } 我希望将这些类的实例序列化为以下示例JSON: JSON格式是由外部API定义的,我无法更改 我已经发现我可以使用自定义序列化程序对列表字段进行注释,例如: @JsonSerialize(using = PeopleSerializer.class) private List<Person>
public class City {
private String title;
private List<Person> people;
}
我希望将这些类的实例序列化为以下示例JSON:
JSON格式是由外部API定义的,我无法更改
我已经发现我可以使用自定义序列化程序对列表字段进行注释,例如:
@JsonSerialize(using = PeopleSerializer.class)
private List<Person> people;
我还研究了使用Jackson的界面,但这不适合展开嵌套的列表对象
- 见:
@JsonUnwrapped
,但它不是设计用于列表的
相关职位
@JsonSerialize(using = CitySerializer.class)
public class City {
private String title;
@JsonIgnore
private List<Person> people;
}
@JsonSerialize(使用=CitySerializer.class)
公营城市{
私有字符串标题;
@杰索尼奥雷
私人名单;
}
…然后
public class CitySerializer extends JsonSerializer<City> {
private static final int START_INDEX = 1;
@Override
public void serialize(City city,
JsonGenerator generator,
SerializerProvider provider) throws IOException {
generator.writeStartObject();
// Write all properties (except ignored)
JavaType javaType = provider.constructType(City.class);
BeanDescription beanDesc = provider.getConfig().introspect(javaType);
JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanSerializer(provider,
javaType,
beanDesc);
serializer.unwrappingSerializer(null).serialize(value, jgen, provider);`
// Custom serialization of people
List<Person> people = city.getPeople();
for (int i = 0; i < people.size(); ++i) {
Person person = people.get(i);
int index = i + START_INDEX;
serialize(person, index, generator);
}
generator.writeEndObject();
}
private void serialize(Person person, int index, JsonGenerator generator) throws IOException {
generator.writeStringField(getIndexedFieldName("personName", index),
person.getName());
generator.writeNumberField(getIndexedFieldName("personAge", index),
person.getAge());
}
private String getIndexedFieldName(String fieldName, int index) {
return fieldName + "_" + index;
}
}
公共类CitySerializer扩展了JsonSerializer{
私有静态最终int START_索引=1;
@凌驾
公共空间连载(城市,
JSONG发电机,
SerializerProvider提供程序)引发IOException{
generator.writeStartObject();
//写入所有属性(忽略的除外)
JavaType JavaType=provider.constructType(City.class);
BeanDescription beanDesc=provider.getConfig().introspect(javaType);
JsonSerializer serializer=BeanSerializerFactory.instance.findBeanSerializer(提供程序,
javaType,
beanDesc);
序列化(值,jgen,provider)`
//自定义人员序列化
List people=city.getPeople();
for(int i=0;i
您可以使用BeanSerializerModifier
直接修改属性名称和值的写入方式。使用它,您可以检测是否存在自定义注释,在本例中,我创建了一个名为@flattcollection
的注释。当存在注释时,数组或集合不是使用普通方法编写的,而是由自定义属性编写器(FlatCollectionPropertyWriter
)编写的
这个注释可能会在2d数组或其他边缘情况下中断,我还没有测试过它们,但您可以为它们编写代码而不会遇到太多麻烦,至少会抛出一个有意义的错误
这是完整的工作代码。值得注意的是
- FlatterCollectionSerializerModifier.changeProperties
- FlatCollectionPropertyWriter.serializeAsField
- 我为你准备的两个托多
{
"titleCity" : "New York",
"personName_1" : "Foo",
"personAge_1" : 123,
"personName_2" : "Baz",
"personAge_2" : 22
}
代码:
import com.fasterxml.jackson.core.JsonGenerator;
导入com.fasterxml.jackson.databind.*;
导入com.fasterxml.jackson.databind.ser.*;
导入com.fasterxml.jackson.databind.util.NameTransformer;
导入java.lang.annotation.ElementType;
导入java.lang.annotation.Retention;
导入java.lang.annotation.RetentionPolicy;
导入java.lang.annotation.Target;
导入java.util.*;
公共类SO45698499{
公共静态void main(字符串[]args)引发异常{
ObjectWriter writer=createMapper().writerWithDefaultPrettyPrinter();
String val=writer.writeValueAsString(纽约),
数组.asList(新人(“Foo”,123),新人(“Baz”,22));
系统输出打印项次(val);
}
/**
*构造映射器时要考虑序列化器修饰符
*@返回
*/
公共静态对象映射器createMapper(){
FlattCollectionSerializerModifier修饰符=新的FlattCollectionSerializerModifier();
SerializerFactory sf=BeanSerializerFactory.instance.withSerializerModifier(修饰符);
ObjectMapper mapper=新的ObjectMapper();
mapper.setSerializerFactory(sf);
返回映射器;
}
@目标({ElementType.ANNOTATION_TYPE,ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
@保留(RetentionPolicy.RUNTIME)
公共@interface集合{
}
/**
*查找集合注释并修改bean编写器
*/
公共静态类FlattCollectionSerializerModifier扩展BeanSerializerModifier{
@凌驾
公共列表更改属性(SerializationConfig配置、BeanDescription beanDesc、列表beanProperties){
对于(int i=0;iJsonGenerationException: Can not write a field name, expecting a value
@JsonSerialize(using = CitySerializer.class)
public class City {
private String title;
@JsonIgnore
private List<Person> people;
}
public class CitySerializer extends JsonSerializer<City> {
private static final int START_INDEX = 1;
@Override
public void serialize(City city,
JsonGenerator generator,
SerializerProvider provider) throws IOException {
generator.writeStartObject();
// Write all properties (except ignored)
JavaType javaType = provider.constructType(City.class);
BeanDescription beanDesc = provider.getConfig().introspect(javaType);
JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanSerializer(provider,
javaType,
beanDesc);
serializer.unwrappingSerializer(null).serialize(value, jgen, provider);`
// Custom serialization of people
List<Person> people = city.getPeople();
for (int i = 0; i < people.size(); ++i) {
Person person = people.get(i);
int index = i + START_INDEX;
serialize(person, index, generator);
}
generator.writeEndObject();
}
private void serialize(Person person, int index, JsonGenerator generator) throws IOException {
generator.writeStringField(getIndexedFieldName("personName", index),
person.getName());
generator.writeNumberField(getIndexedFieldName("personAge", index),
person.getAge());
}
private String getIndexedFieldName(String fieldName, int index) {
return fieldName + "_" + index;
}
}
{
"titleCity" : "New York",
"personName_1" : "Foo",
"personAge_1" : 123,
"personName_2" : "Baz",
"personAge_2" : 22
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.*;
import com.fasterxml.jackson.databind.util.NameTransformer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.*;
public class SO45698499 {
public static void main(String [] args) throws Exception {
ObjectWriter writer = createMapper().writerWithDefaultPrettyPrinter();
String val = writer.writeValueAsString(new City("New York",
Arrays.asList(new Person("Foo", 123), new Person("Baz", 22))));
System.out.println(val);
}
/**
* Constructs our mapper with the serializer modifier in mind
* @return
*/
public static ObjectMapper createMapper() {
FlattenCollectionSerializerModifier modifier = new FlattenCollectionSerializerModifier();
SerializerFactory sf = BeanSerializerFactory.instance.withSerializerModifier(modifier);
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializerFactory(sf);
return mapper;
}
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface FlattenCollection {
}
/**
* Looks for the FlattenCollection annotation and modifies the bean writer
*/
public static class FlattenCollectionSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
for (int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer = beanProperties.get(i);
FlattenCollection annotation = writer.getAnnotation(FlattenCollection.class);
if (annotation != null) {
beanProperties.set(i, new FlattenCollectionPropertyWriter(writer));
}
}
return beanProperties;
}
}
/**
* Instead of writing a collection as an array, flatten the objects down into values.
*/
public static class FlattenCollectionPropertyWriter extends BeanPropertyWriter {
private final BeanPropertyWriter writer;
public FlattenCollectionPropertyWriter(BeanPropertyWriter writer) {
super(writer);
this.writer = writer;
}
@Override
public void serializeAsField(Object bean,
JsonGenerator gen,
SerializerProvider prov) throws Exception {
Object arrayValue = writer.get(bean);
// lets try and look for array and collection values
final Iterator iterator;
if(arrayValue != null && arrayValue.getClass().isArray()) {
// deal with array value
iterator = Arrays.stream((Object[])arrayValue).iterator();
} else if(arrayValue != null && Collection.class.isAssignableFrom(arrayValue.getClass())) {
iterator = ((Collection)arrayValue).iterator();
} else {
iterator = null;
}
if(iterator == null) {
// TODO: write null? skip? dunno, you gonna figure this one out
} else {
int index=0;
while(iterator.hasNext()) {
index++;
Object value = iterator.next();
if(value == null) {
// TODO: skip null values and still increment or maybe dont increment? You decide
} else {
// TODO: OP - update your prefix/suffix here, its kinda weird way of making a prefix
final String prefix = value.getClass().getSimpleName().toLowerCase();
final String suffix = "_"+index;
prov.findValueSerializer(value.getClass())
.unwrappingSerializer(new FlattenNameTransformer(prefix, suffix))
.serialize(value, gen, prov);
}
}
}
}
}
public static class FlattenNameTransformer extends NameTransformer {
private final String prefix;
private final String suffix;
public FlattenNameTransformer(String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
}
@Override
public String transform(String name) {
// captial case the first letter, to prepend the suffix
String transformedName = Character.toUpperCase(name.charAt(0)) + name.substring(1);
return prefix + transformedName + suffix;
}
@Override
public String reverse(String transformed) {
if (transformed.startsWith(prefix)) {
String str = transformed.substring(prefix.length());
if (str.endsWith(suffix)) {
return str.substring(0, str.length() - suffix.length());
}
}
return null;
}
@Override
public String toString() { return "[FlattenNameTransformer('"+prefix+"','"+suffix+"')]"; }
}
/*===============================
* POJOS
===============================*/
public static class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public static class City {
private String titleCity;
private List<Person> people;
public City(String title, List<Person> people) {
this.titleCity = title;
this.people = people;
}
public String getTitleCity() {
return titleCity;
}
public void setTitleCity(String titleCity) {
this.titleCity = titleCity;
}
@FlattenCollection
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
}
}