Как эффективно сопоставить org.json.JSONObject с POJO?

Этот вопрос должен был быть задан раньше, но я не смог его найти.

Я использую стороннюю библиотеку для извлечения данных в формате JSON. Библиотека предлагает мне данные как org.json.JSONObject. Я хочу сопоставить это JSONObject с POJO (обычный старый объект Java) для более простого доступа/кода.

Для сопоставления в настоящее время я использую ObjectMapper из библиотеки Джексона следующим образом:

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

Насколько я понимаю, приведенный выше код может быть оптимизирован значительно, поскольку в настоящее время данные в JSONObject, которые уже разобраны, снова подаются в цепочку сериализации-десериализации с помощью метода JSONObject.toString(), а затем в ObjectMapper.

Я хочу избежать этих двух преобразований (toString() и разбора). Есть ли способ использовать JSONObject для сопоставления своих данных непосредственно с POJO?

Ответы

Ответ 1

Поскольку у вас есть абстрактное представление некоторых данных JSON (объект org.json.JSONObject), и вы планируете использовать библиотеку Джексона - который имеет свое собственное абстрактное представление данных JSON (com.fasterxml.jackson.databind.JsonNode), то преобразование из одного представления в другое спасло бы вас от анализа, serialize-parse process. Таким образом, вместо использования метода 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;
}

Обратите внимание, что, хотя Jackson JsonNode может представлять некоторые дополнительные типы (например, BigInteger, Decimal и т.д.), они не нужны, так как приведенный выше код охватывает все, что может <<2 → .

Ответ 2

Если вы не связаны с Джексоном, вы можете использовать удобную библиотеку google-gson в качестве альтернативы. Он требует только одну банку и очень прост в использовании:

Преобразование java-объекта в строку JSON:

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

Создание java-объекта из строки JSON:

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

Я не знаю о производительности по сравнению с Джексоном, но это сложно сделать проще... Gson - стабильная и широко используемая библиотека.

См. https://code.google.com/p/google-gson/

Ответ 3

Добавление ответа на старый вопрос, но...

Джексон может связываться с/из типов 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, ни один из способов не является издержками.