web-dev-qa-db-ja.com

org.json.JSONObjectをPOJOに効率的にマップする方法は?

この質問は以前に聞かれたに違いありませんが、見つけられませんでした。

サードパーティのライブラリを使用して、JSON形式でデータを取得しています。ライブラリは、データを_org.json.JSONObject_として提供します。アクセスを簡単にするために、このJSONObjectPOJO(Plain Old Java Object) にマッピングしたいと思います。

マッピングには、現在、ジャクソンライブラリのObjectMapperを次のように使用しています。

_JSONObject jsonObject = //...
ObjectMapper mapper = new ObjectMapper();
MyPojoClass myPojo = mapper.readValue(jsonObject.toString(), MyPojoClass.class);
_

私の理解では、上記のコードは大幅に最適化することができます。現在、JSONObjectのデータは既に解析されており、JSONObject.toString()メソッドと次にObjectMapperに。

これらの2つの変換(toString()と解析)を避けたいです。 JSONObjectを使用してそのデータをPOJOに直接マップする方法はありますか?

24
Daniel S.

JSONデータの抽象的な表現があるため( org.json.JSONObject object)そして、JSONデータの独自の抽象表現を持つジャクソンライブラリを使用する予定です( com.fasterxml.jackson.databind.JsonNode )-ある表現から別の表現への変換は、parse-serialize-parseプロセスからあなたを救います。したがって、readValueを受け入れるStringメソッドを使用する代わりに、JsonParserを受け入れる このバージョン を使用します。

JSONObject jsonObject = //...
JsonNode jsonNode = convertJsonFormat(jsonObject);
ObjectMapper mapper = new ObjectMapper();
MyPojoClass myPojo = mapper.readValue(new TreeTraversingParser(jsonNode), MyPojoClass.class);

JSONは非常に単純な形式なので、convertJsonFormatを手作業で作成するのは難しくありません。これが私の試みです。

static JsonNode convertJsonFormat(JSONObject json) {
    ObjectNode ret = JsonNodeFactory.instance.objectNode();

    @SuppressWarnings("unchecked")
    Iterator<String> iterator = json.keys();
    for (; iterator.hasNext();) {
        String key = iterator.next();
        Object value;
        try {
            value = json.get(key);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        if (json.isNull(key))
            ret.putNull(key);
        else if (value instanceof String)
            ret.put(key, (String) value);
        else if (value instanceof Integer)
            ret.put(key, (Integer) value);
        else if (value instanceof Long)
            ret.put(key, (Long) value);
        else if (value instanceof Double)
            ret.put(key, (Double) value);
        else if (value instanceof Boolean)
            ret.put(key, (Boolean) value);
        else if (value instanceof JSONObject)
            ret.put(key, convertJsonFormat((JSONObject) value));
        else if (value instanceof JSONArray)
            ret.put(key, convertJsonFormat((JSONArray) value));
        else
            throw new RuntimeException("not prepared for converting instance of class " + value.getClass());
    }
    return ret;
}

static JsonNode convertJsonFormat(JSONArray json) {
    ArrayNode ret = JsonNodeFactory.instance.arrayNode();
    for (int i = 0; i < json.length(); i++) {
        Object value;
        try {
            value = json.get(i);
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
        if (json.isNull(i))
            ret.addNull();
        else if (value instanceof String)
            ret.add((String) value);
        else if (value instanceof Integer)
            ret.add((Integer) value);
        else if (value instanceof Long)
            ret.add((Long) value);
        else if (value instanceof Double)
            ret.add((Double) value);
        else if (value instanceof Boolean)
            ret.add((Boolean) value);
        else if (value instanceof JSONObject)
            ret.add(convertJsonFormat((JSONObject) value));
        else if (value instanceof JSONArray)
            ret.add(convertJsonFormat((JSONArray) value));
        else
            throw new RuntimeException("not prepared for converting instance of class " + value.getClass());
    }
    return ret;
}

ジャクソンのJsonNodeはいくつかの追加の型(BigIntegerDecimalなど)を表すことができますが、上記のコードはJSONObjectは表すことができます。

27
mgibsonbr

ジャクソンと提携していない場合は、代わりに便利なgoogle-gsonライブラリを使用できます。必要なjarは1つのみで、使用方法は非常に簡単です。

JavaオブジェクトをJSON文字列に変換:

  String json_string = new Gson().toJson(an_object);

Javaオブジェクトの作成:

  MyObject obj = new Gson().fromJson(a_json_string, MyObject.class);

ジャクソンと比較してパフォーマンスについては知りませんが、これよりも簡単にするのは難しいです... Gsonは安定した広く使用されているライブラリです。

https://code.google.com/p/google-gson/ を参照してください

24

古い質問に答えを追加しますが、...

Jacksonはorg.json型との間でバインドできます。一般に、効率的に(実際にはそうではないが)JSONにシリアライズしデシリアライズすることにより、バインド可能な任意のタイプ間で変換できます。

JsonOrgModule が登録されている場合、ObjectMapperから直接変換を実行できます。

@Test
public void convert_from_jsonobject() throws Exception {
    JSONObject obj = new JSONObject().put("value", 3.14);
    ObjectMapper mapper = new ObjectMapper().registerModule(new JsonOrgModule());
    PojoData data = mapper.convertValue(obj, PojoData.class);
    assertThat(data.value, equalTo(3.14));
}

@Test
public void convert_to_jsonobject() throws Exception {
    PojoData data = new PojoData();
    data.value = 3.14;
    ObjectMapper mapper = new ObjectMapper().registerModule(new JsonOrgModule());
    JSONObject obj = mapper.convertValue(data, JSONObject.class);
    assertThat(obj.getDouble("value"), equalTo(3.14));
}

public static final class PojoData {
    public double value;
}

私はこれが効果的にシリアライズだと言いましたか?確かに、入力オブジェクトをTokenBufferにシリアル化します。これは、JSON解析イベントのストリームを表しますが、入力からのデータを主に参照できるため、文字列の構築などの影響は少なくなります。次に、このストリームをデシリアライザーに送り、出力オブジェクトを生成します。

そのため、JSONObjectをJsonNodeに変換するという提案に多少似ていますが、より一般的です。実際に効率的であるかどうかに関係なく、測定が必要になります。中間としてJsonNodeを構築するか、TokenBufferを構築するかのどちらでも、どちらの方法でもオーバーヘッドは発生しません。

16
araqnid