假设我有以下三个类(为了简洁省略了getter和setter):
@JsonAutoDetect
public class InfoCollection{
private InfoType1 info1;
private InfoType2 info2;
}
@JsonAutoDetect
public class InfoType1{
private String fieldA;
}
@JsonAutoDetect
public class InfoType2{
private String fieldB;
}
我正在尝试编写JsonSerializer.serialize()
函数,以此格式序列化InfoCollection
对象:
{
"allInfo":{
"fieldA":"foo",
"fieldB":"bar"
}
}
这就是我现在所拥有的:
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("allInfo");
jsonGenerator.writeObject(myInfoCollection.getInfo1());
jsonGenerator.writeObject(myInfoCollection.getInfo2());
jsonGenerator.writeEndObject();
导致以下异常:
org.codehaus.jackson.JsonGenerationException: Can not start an object, expecting field name
我错过了一些小事,或者我是否完全以错误的方式解决这个问题?
注意:到目前为止,提出的一些解决方案涉及编写InfoType1
和InfoType2
的每个字段。我正在寻找一个不需要这个的解决方案,因为我想在很多领域的大型课程上使用该解决方案。
答案 0 :(得分:9)
您应该调用writeFieldName("allInfo")
而不是调用writeObjectFieldStart("allInfo")
,因为“allInfo”是另一个JSON对象。因此,您的自定义序列化程序应该采用以下方式:
public void serialize(InfoCollection infoCollection, JsonGenerator jgen, SerializerProvider provider) throws IOException{
jgen.writeStartObject();
jgen.writeObjectFieldStart("allInfo");
jgen.writeObjectField("fieldA", infoCollection.getInfo1().getFieldA());
jgen.writeObjectField("fieldB", infoCollection.getInfo2().getFieldB());
jgen.writeEndObject();
jgen.writeEndObject();
}
或者您可以尝试基于注释的方法:
@JsonRootName("allInfo")
public class InfoCollection {
@JsonUnwrapped
private InfoType1 info1;
@JsonUnwrapped
private InfoType2 info2;
/* getters, setters */
}
(您需要启用SerializationConfig.Feature.WRAP_ROOT_VALUE
功能才能实现此功能。请参阅Serialization features)
答案 1 :(得分:4)
将来,当您有堆栈跟踪时,请告诉我们问题出现在哪一行。
那就是说,修复可能是:
jsonGenerator.writeStartObject();
jsonGenerator.writeFieldName("allInfo");
jsonGenerator.writeStartObject(); // start nested object
jsonGenerator.writeFieldName("fieldA"); // start field
jsonGenerator.writeObject(myInfoCollection.getInfo1().fieldA);
jsonGenerator.writeFieldName("fieldB"); // start fieldB
jsonGenerator.writeObject(myInfoCollection.getInfo2().fieldB);
jsonGenerator.writeEndObject(); // end nested object
jsonGenerator.writeEndObject();
使用包装器对象的解决方案:
@JsonAutoDetect
public class Wrapper {
private transient InfoCollection data; // transient makes Jackson ignore this
public String getFieldA() { return data.info1.fieldA; }
public String getFieldB() { return data.info1.fieldB; }
}
这让杰克逊只看到你想要的东西以及你想要的东西。
或者,使用反射递归收集所有字段及其名称:
List<Pair<String, Object>> data = collectFields( myInfoCollection );
collectFields
应检查所有字段并将所有内容添加到列表中,该列表可以是原始字段,也可以是field.getType().getName().startsWith("java.lang")
或您需要的任何其他规则。
如果该字段是引用,则递归调用collectFields()
。
获得列表后,只需在循环中调用jsonGenerator
即可编写结果。