web-dev-qa-db-ja.com

JSON Jacksonシリアル化のルートキーとしてクラス名を使用する

私はpojoを持っていると仮定します:

import org.codehaus.jackson.map.*;

public class MyPojo {
    int id;
    public int getId()
    { return this.id; }

    public void setId(int id)
    { this.id = id; }

    public static void main(String[] args) throws Exception {
        MyPojo mp = new MyPojo();
        mp.setId(4);
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
        System.out.println(mapper.getSerializationConfig().isEnabled(SerializationConfig.Feature.WRAP_ROOT_VALUE));
        System.out.println(mapper.writeValueAsString(mp));
    }
}

Jackson ObjectMapperを使用してシリアル化すると、

true
{"id":4}

でも私はしたい

true
{"MyPojo":{"id":4}}

私はいろいろ調べましたが、ジャクソンのドキュメントは本当に整理されておらず、ほとんど時代遅れです。

27
DanInDC

ジャクソン注釈を追加することにより@JsonTypeInfoクラスレベルでは、期待される出力を得ることができます。クラスに変更なしを追加しました。

package com.test.jackson;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;

import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME)
public class MyPojo {
    // Remain same as you have
}

出力:

{
    "MyPojo": {
        "id": 4
    }
}
31
Arun Prakash

私はjacksonを使用していませんが、検索すると、この構成があなたが望むものであるように見えました: WRAP_ROOT_VALUE

アノテーションイントロスペクター(特に@XmlRootElementを使用するJAXBの場合)によって決定される、「ルート名」としてのキーである単一のプロパティJSONオブジェクト内にルート値(通常はJSONオブジェクトですが、任意のタイプ)をラップできるようにする機能.name)またはフォールバック(非修飾クラス名)。この機能は主にJAXBとの互換性を目的としています。

デフォルト設定はfalseです。つまり、ルート値はラップされません。

マッパーを設定できるように:

objectMapper.configure(SerializationConfig.Feature.WRAP_ROOT_VALUE, true);

私はそれがあなたを助けることを願っています...

25
Aito

以下はこれを達成する方法です

Map<String, MyPojo> singletonMap = Collections.singletonMap("mypojo", mp);
System.out.println(mapper.writeValueAsString(singletonMap));

出力 {"mypojo":{"id":4}}

ここでの利点は、jsonオブジェクトのルートキーのon名を指定できることです。上記のコードでは、mypojoがルートキーになります。このアプローチは、jsonオブジェクトの反復にMustache.jsのようなスクリプトテンプレートJavaを使用する場合に最も役立ちます。

12
Anish George

これを実現するには、クラスでJsonTypeInfoアノテーションを使用する必要があり、特に WRAPPER_OBJECT

@JsonTypeName("foo")                                                                                         
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT ,use = JsonTypeInfo.Id.NAME)

public class Bar(){
)
4
user2083529

このための素晴らしい注釈もあります:

@JsonRootName(value = "my_pojo")
public class MyPojo{
  ...
}

生成します:

{
  "my_pojo" : {...}
}
3
JeanValjean

私が使用した別の方法があり、それは私のために働いた。私はサードパーティのjarファイルを使用しているため、注釈を制御できません。だから、ちょっとしたハックで書く必要がありました。

オーバーライド:org.codehaus.jackson.map.ser.BeanSerializerFactory.findBeanProperties(SerializationConfig, BasicBeanDescription)

以下のようにプロパティを追加します

List<BeanPropertyWriter> props = super.findBeanProperties(config, beanDesc);
BeanPropertyWriter bpw = null;
try {
     Class cc = beanDesc.getType().getRawClass();
     Method m = cc.getMethod("getClass", null);
     bpw = new BeanPropertyWriter("$className", null, null, m, null,true, null);
} catch (SecurityException e) {
  // TODO
} catch (NoSuchMethodException e) {
  // TODO
}
props.add(bpw);
return props;

このようにして、より多くの制御を取得し、他の種類のフィルターも実行できます。

1
shailendra

可能な限り単純な解決策はどうですか。次のようなラッパークラスを使用するだけです。

class Wrapper {
   public MyPojo MyPojo;
}

コードのラッピング/アンラッピング?

これを超えて、なぜこのような追加のjsonオブジェクトエントリが必要なのかを知るのに役立ちますか?これは、xml apiを介してjsonをエミュレートするライブラリによって行われます(xmlからjsonへの変換のため、xmlとjsonの間のインピーダンスのため)が、純粋なjsonソリューションの場合、通常は必要ありません。

実際のタイプが何であるかを理解できるようにするためですか?もしそうなら、ジャクソンにそれを自動的に処理させるために、おそらくあなたは有効なポリモーフィック型情報を考慮することができますか? (詳細については、 1.5リリースノート 、PTHのエントリを参照)。

1
StaxMan

これに対するOPの解決策を聞くことに興味があります。 RESTful Webサービスがクライアント用にXMLまたはJSONとしてオブジェクトをシリアル化する場合、同様の問題が発生します。 Javascriptクライアントは、解析できるようにラッピングタイプを知る必要があります。型をURIパターンに結合することはオプションではありません。

ありがとう。

編集:Spring MappingJacksonJsonMarshallerがマーシャリング時にラッピングクラスを追加することに気づいたので、デバッグでコードをステップ実行し、Springがキーがラッピング名であり、値がオブジェクト。そこで、JacksonJaxbJsonProviderを拡張し、writeTo()メソッドをオーバーライドして、次を追加しました。

HashMap<String, Object> map = new HashMap<String, Object>();
map.put(value.getClass().getSimpleName(), value);
super.writeTo(map, type, genericType, annotations, mediaType, httpHeaders,entityStream);

ちょっとしたハックですが、うまく機能します。

0
Tony

私は経験を通じて、すべてのJSONがバックエンドタイプ(文字列として)とフロントエンドでレンダリングするために使用されるコンポーネントタイプ(angularまたはVueのようなものを使用する場合)の両方を含むことをお勧めします。

これを行う理由は、単一のコードセットでさまざまなタイプを処理できるようにするためです。

たとえば、vueでは、データにUIコンポーネントの名前を含めることで、特に、親テンプレートの1つのタグのみを使用して、さまざまなタイプの子のリストを画面に表示できます。

  <component :is="child.componentType"/>.

バックエンドシステムとWebサービスの場合-着信ペイロードに基づいて適切なプロセッサクラスを検索することにより、すべてのWebサービスのロギング、監査、および例外処理を提供する単一のWebサービスプロセッサクラスを使用することを好みます。これにより、すべてのWebサービスの実装がまったく同じに見えます(約3行のコード)。サービスコードごとに記述することなく、呼び出しのライフサイクルを通じて詳細なイベントログを取得できます。

型がJSONをラップすると、自己文書化されます。表示されるのがプロパティだけである場合、対応するエンドポイントが見つかるまで、何を見ているのかわかりません。

データ駆動型ソフトウェアを作成する場合、処理対象を特定できることが基本的な要件です。

0
@JsonTypeInfo(include=As.WRAPPER_OBJECT, use=Id.NAME) 

この注釈は、Arun Prakashが示唆するように、完全に機能します。私はこの形式でjsonを取得しようとしていました。

{"Rowset":{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

しかし、このようになる:

{"ROW":{"receiptno":"881604199388936","status":"SUCCESS"}}}

今、その注釈は私の問題を解決しました。

0
Naushad Idrisi

withRootNameを使用します。

objectMapper.writer().withRootName(MyPojo.class.getName());
0
winry