考虑到以下几点。。
public class City {
private String title;
private List<Person> people;
}
...
public class Person {
private String name;
private int age;
}
我想让Jackson将类的实例序列化到以下示例JSON:
{
"title" : "New York",
"personName_1" : "Jane Doe",
"personAge_1" : 42,
"personName_2" : "John Doe",
"personAge_2" : 23
}
JSON格式是由外部API定义的,我无法更改。
我已经发现我可以使用自定义序列化程序对列表字段进行注释,例如:
@JsonSerialize(using = PeopleSerializer.class)
private List<Person> people;
...这是我尝试的一个基本实现:
public class PeopleSerializer extends JsonSerializer<List<Person>> {
private static final int START_INDEX = 1;
@Override
public void serialize(List<Person> people,
JsonGenerator generator,
SerializerProvider provider) throws IOException {
for (int i = 0; i < people.size(); ++i) {
Person person = people.get(i);
int index = i + START_INDEX;
serialize(person, index, generator);
}
}
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;
}
}
然而,这在以下情况下失败:
JsonGenerationException: Can not write a field name, expecting a value
我还研究了使用Jackson的转换器接口,但这不适用于展开嵌套列表对象。
我也知道@JsonUnwrapped
,但它不是为列表设计的。
基于这个链接,我怀疑字段级注释只代表写入值,而不是整个属性。
一个(相当笨拙的)解决方法可能是为整个City类提供一个自定义序列化程序:
@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;
}
}
您可以使用BeanSerializerModifier
直接修改属性名称和值的写入方式。使用它,您可以检测是否存在自定义注释,在本例中,我创建了一个名为@flattcollection
的注释。当存在注释时,数组或集合不是使用常规方法编写的,而是由自定义属性编写器(FlatCollectionPropertyWriter
)编写的。
这个注释可能会在2d数组或其他边缘情况下中断,我还没有测试过它们,但您可能可以为它们编写代码,而不会遇到太多麻烦,至少会抛出一个有意义的错误。
这是完整的工作代码。值得注意的点是
输出:
{
"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;
}
}
}
问题内容: 如何将作为地图的属性序列化为地图的值列表?我已经能够使用吸气剂上的注释进行其他简单的转换。但是,我不确定我想做什么。 问题答案: 我们需要类似的东西,在我们的案例中,我们使用了您所评论的自定义项,这很简单: 使用它的代码:
我有这样一个实体: 和相关的JSON: 我试图在Spring MVC@Controller中以如下方式对其进行反序列化: 添加
我目前正在开发一个Java web应用程序,它使用Magento REST API公开的JSON数据。api返回的数据示例如下: 我的应用程序中有一个Java类,如下所示: 我想对数据进行反序列化,并将其转换为,但我总是得到以下错误: 这是我用来将JSON响应反序列化为ArrayList的语句行: 有人能分享一些见解吗?我看到一些例子,返回的JSON对象前面没有任何ID。那是因为我做错了什么吗?非
问题内容: 我需要将一些对象序列化为JSON并发送到WebService。如何使用org.json库?否则我将不得不使用另一个?这是我需要序列化的类: 我只放了类的变量和构造函数,但也有getter和setter方法。所以如果有人可以帮忙 问题答案: 没有注释的简单方法是使用Gson库 就那么简单:
我正在编写一个Java spring boot mvc应用程序,它可以导出/导入数据。我写了一个包装类,它应该为学生类序列化/反序列化数据。它适用于导出,但在导入过程中出现错误 下面是我的maven jackson依赖项: 最后是转换的方法: 上面提到了Mapper.ReadValue(stream,typeReference)处的MismatchInputException ObjectMapp
我需要反序列化以下json: 将它的< code>id属性设置为< code>foo_id json属性。 我需要在自定义反序列化程序中执行此操作。实现这一点最简单的方法是什么? 我想以某种方式将json“转换”为 然后将此委托给杰克逊。 在本例中,对象的类型为Foo,但其他对象可能不属于此类。另外,在本例中,json是一个数字,但如果它也是一个字符串,我希望支持。所以,我需要一种通用的方法来做到