11

假设我有以下三个类(为简洁起见,省略了 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。我正在寻找一个不需要这个的解决方案,因为我想在具有许多字段的大型类上使用该解决方案。

4

2 回答 2

11

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功能才能使其正常工作。请参阅序列化功能

于 2013-02-12T16:15:06.260 回答
4

In the future, when you have a stack trace, let us know in which line the problem shows up.

That said, the fix is probably:

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();

Solution using a wrapper object:

@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; }
}

That makes Jackson see only what you want and how you want it.

Alternatively, use reflection to recursively collect all fields and their names:

List<Pair<String, Object>> data = collectFields( myInfoCollection );

collectFields should examine all fields and add everything to the list which is either a primitive or, say, where field.getType().getName().startsWith("java.lang") or any other rules you need.

If the field is a reference, call collectFields() recursively.

When you have the list, just call jsonGenerator in a loop to write the results.

于 2013-02-12T15:58:48.510 回答