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

46

我正在使用第三方库以JSON格式获取数据。该库将数据提供给我的方式是一个org.json.JSONObject对象。我想将这个JSONObject对象映射到POJO(普通的旧Java对象)中,以简化访问/代码。

为了进行映射,我目前使用Jackson库中的ObjectMapper,如下:

JSONObject jsonObject = //...
ObjectMapper mapper = new ObjectMapper();
MyPojoClass myPojo = mapper.readValue(jsonObject.toString(), MyPojoClass.class);
据我所知,上述代码可以进行大幅度的优化,因为当前已经解析的JSONObject中的数据再次通过JSONObject.toString()方法进入序列化-反序列化链,然后传递给ObjectMapper
我想避免这两个转换(toString()和解析)。有没有一种方法可以直接使用JSONObject将其数据映射到POJO?
4个回答

46
由于您拥有一些JSON数据的抽象表示(一个org.json.JSONObject对象),并且您计划使用Jackson库,该库具有自己的JSON数据的抽象表示(com.fasterxml.jackson.databind.JsonNode),因此从一个表示转换为另一个表示将使您免于进行解析-序列化-解析的过程。因此,您将使用接受JsonParserthis version方法,而不是使用接受StringreadValue方法。
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可以表示一些额外的类型(例如BigIntegerDecimal等),但它们并不是必需的,因为上面的代码涵盖了JSONObject可以表示的所有内容。

1
我已经尝试过它并且它有效。根据我的基准测试,它的性能提升约为30%。谢谢! - Daniel S.
顺便提一下,convertJsonFormat对于else if (value instanceof JSONArray) ret.put(key, convertJsonFormat((JSONArray) value))无效。 - DJphy
我认为Github的链接不再有效 - Cugomastik

36

回答一个旧问题,但是...

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,都存在开销。


5
这是该帖子上最好的答案,谢谢分享。 - Alex Barker

28

如果您没有与杰克逊绑定,可以使用方便的google-gson库作为替代方案。它只需要一个jar包,非常简单易用:

将Java对象转换为JSON字符串:

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

从JSON字符串创建Java对象:

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

我不知道与Jackson相比它的性能如何,但它很难比这更简单...... Gson是一个稳定且广泛使用的库。

请参见https://code.google.com/p/google-gson/


8
抱歉,这不是对我的问题的回答。你的代码也无法避免双重解析。此外,无论是使用Jackson还是Gson,代码复杂度都是相同的。在Jackson中,您的代码将会是MyObject obj = new ObjectMapper().readValue(a_json_string, MyObject.class);,真正的区别只是类名长度为“Gson”和“ObjectMapper”的差异。 - Daniel S.
1
它回答了我的问题和其他17个人的问题。 - Alex Barker
2
问题是“如何将org.json.JSONObject转换为POJO”。你的回答是关于“使用Gson有多有趣”。 - chill appreciator

3
使用Gson可以更简单地实现此功能。
JSONObject jsonObject = //...

PojoObject objPojo = new Gson().fromJson(jsonObject.toString(), PojoObject.class);

这对我有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接