如何有效地将 org.json.JSONObject 映射到 POJO?

2022-09-01 01:49:07

这个问题以前一定被问过,但我找不到它。

我正在使用第三方库来检索JSON格式的数据。该库将数据作为.我想将其映射到POJO(普通旧Java对象)以简化访问/代码。org.json.JSONObjectJSONObject

对于映射,我目前以这种方式使用来自 Jackson 库的 映射:ObjectMapper

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

据我所知,上面的代码可以得到显着的优化,因为目前已经解析的数据再次被输入到序列化 - 反序列化链中,然后进入.JSONObjectJSONObject.toString()ObjectMapper

我想避免这两个转换(和解析)。有没有办法使用 将其数据直接映射到 POJO?toString()JSONObject


答案 1

由于您拥有某些 JSON 数据的抽象表示形式(org.json.JSONObject 对象),并且您计划使用 Jackson 库 - 该库具有自己的 JSON 数据抽象表示形式 (com.fasterxml.jackson.databind.JsonNode) - 那么从一种表示形式到另一种表示形式的转换将使您免于 parse-serialize-parse 过程。因此,与其使用接受 的方法,不如使用接受readValueStringJsonParser

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 可以表示一些额外的类型(如 、 等),但它们不是必需的,因为上面的代码涵盖了可以表示的所有内容。JsonNodeBigIntegerDecimalJSONObject


答案 2

添加旧问题的答案,但是...

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,这两种方法都不是没有开销的。