11

次の 3 つのクラスがあるとします (簡潔にするためにゲッターとセッターは省略しています)。

@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

「allInfo」は別の JSON オブジェクトであるため、呼び出す代わりに呼び出すwriteFieldName("allInfo")必要があります。writeObjectFieldStart("allInfo")したがって、カスタム シリアライザーは次のようになります。

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

今後、スタック トレースがある場合は、問題が発生している行をお知らせください。

とはいえ、修正はおそらく次のとおりです。

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、ループを呼び出して結果を書き込みます。

于 2013-02-12T15:58:48.510 に答える