web-dev-qa-db-de.com

Wie ordne ich ein org.json.JSONO-Objekt effizient einem POJO zu?

Diese Frage muss vorher gestellt worden sein, aber ich konnte sie nicht finden.

Ich verwende eine Drittanbieter-Bibliothek, um Daten im JSON-Format abzurufen. Die Bibliothek bietet mir die Daten als org.json.JSONObject An. Ich möchte dieses JSONObject einem POJO (Plain Old Java Object) zuordnen, um den Zugriff/Code zu vereinfachen.

Für das Mapping verwende ich derzeit das ObjectMapper aus der Jackson-Bibliothek wie folgt:

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

Nach meinem Verständnis kann der obige Code erheblich optimiert werden, da derzeit die Daten in JSONObject, die bereits analysiert wurden, erneut mit der Methode JSONObject.toString() und in eine Serialisierungs-Deserialisierungs-Kette eingespeist werden dann zum ObjectMapper.

Ich möchte diese beiden Konvertierungen (toString() und parsing) vermeiden. Gibt es eine Möglichkeit, die JSONObject zu verwenden, um die Daten direkt einem POJO zuzuordnen?

24
Daniel S.

Da Sie eine abstrakte Darstellung einiger JSON-Daten haben (ein org.json.JSONObject object) und Sie planen, die Jackson-Bibliothek zu verwenden - die eine eigene abstrakte Darstellung von JSON-Daten hat ( com.fasterxml.jackson.databind.JsonNode ) - dann würde eine Konvertierung von einer Darstellung in die andere Sie vor dem Parse-Serialize-Parse-Prozess bewahren. Anstatt die readValue -Methode zu verwenden, die ein String akzeptiert, verwenden Sie diese Version , die ein JsonParser akzeptiert:

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

JSON ist ein sehr einfaches Format, daher sollte es nicht schwierig sein, das convertJsonFormat von Hand zu erstellen. Hier ist mein Versuch:

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

Beachten Sie, dass die JsonNode von Jackson einige zusätzliche Typen darstellen können (wie BigInteger, Decimal usw.), da der obige Code alles abdeckt, was JSONObject darstellen kann.

27
mgibsonbr

Wenn Sie nicht an Jackson gebunden sind, können Sie alternativ die praktische Google-Gson-Bibliothek verwenden. Es benötigt nur ein Glas und ist sehr einfach zu bedienen:

Konvertieren eines Java -Objekts in eine JSON-Zeichenfolge:

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

Erstellen eines Java -Objekts aus einer JSON-Zeichenfolge:

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

Ich weiß nichts über die Leistung im Vergleich zu Jackson, aber es ist schwer, einfacher zu sein als diese ... Gson ist eine stabile und weit verbreitete Bibliothek.

Siehe https://code.google.com/p/google-gson/

24

Hinzufügen einer Antwort auf eine alte Frage, aber ...

Jackson kann an die org.json-Typen binden. Im Allgemeinen kann es zwischen allen Typen konvertieren, an die es gebunden werden kann, indem effektiv (obwohl nicht tatsächlich) in JSON serialisiert und deserialisiert wird.

Wenn Sie das JsonOrgModule registriert haben, können Sie die Konvertierung einfach direkt aus ObjectMapper ausführen:

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

Ich erwähnte, dass dies eine effektive Serialisierung ist? Das stimmt, es serialisiert das Eingabeobjekt in einen TokenBuffer, der einen Strom von JSON-Parsingereignissen darstellt, jedoch weniger Auswirkungen auf die Erstellung von Zeichenfolgen usw. hat, da es weitgehend auf Daten aus der Eingabe verweisen kann. Anschließend wird dieser Stream einem Deserialisierer zugeführt, um das Ausgabeobjekt zu erstellen.

Daher ähnelt es dem Vorschlag, das JSONObject in einen JsonNode zu konvertieren, ist aber viel allgemeiner. Ob es nun effizienter ist oder nicht, es muss gemessen werden: Entweder Sie erstellen einen JsonNode als Intermediate oder als TokenBuffer.

16
araqnid