2013-12-12 4 views
16

Этот вопрос, должно быть, был задан раньше, но я не мог его найти.Как эффективно сопоставить org.json.JSONObject с POJO?

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

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

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

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

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

+3

Используйте Gson для этого. – Jhanvi

ответ

21

Поскольку у вас есть абстрактное представление некоторых данных JSON (org.json.JSONObject объекта), и вы планируете использовать библиотеку Джексона - которая имеет свое собственное абстрактное представление данных в формате JSON (com.fasterxml.jackson.databind.JsonNode) - тогда преобразование из одного представления в другой спасет вас от процесса parse-serialize-parse. Таким образом, вместо того, чтобы использовать readValue метод, который принимает String, вы будете использовать this version, который принимает 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 может представлять некоторые дополнительные типы (например, BigInteger, Decimal и т.д.) они не нужны, так как выше код включает в себя все, что может представлять JSONObject.

+1

Я пробовал, и он работает. По моим оценкам, он имеет прирост производительности около 30%. Благодаря! –

+0

BTW, convertJsonFormat wont work for else if (value instanceof JSONArray) ret.put (key, convertJsonFormat ((JSONArray) value)); – DJphy

14

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

+1

Извините, это не ответ на мой вопрос. Ваш код также не обойти двойной синтаксический анализ. Более того, сложность кода одинакова, независимо от того, используете ли вы Джексона или Гсона. В Jackson ваш код будет читать «MyObject obj = new ObjectMapper(). ReadValue (a_json_string, MyObject.class);», что на самом деле не имеет значения, кроме длины имени класса 'Gson' и' ObjectMapper'. –

7

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

Джексон может связываться с/из типов 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; 
} 

Я упомянул, что это эффективно serialising? Это верно, он сериализует входной объект в TokenBuffer, который представляет поток событий разбора JSON, но с меньшим воздействием строковых строк и т. Д., Поскольку он может в значительной степени ссылаться на данные с входа. Затем он передает этот поток в десериализатор для создания выходного объекта.

Итак, это похоже на предложение конвертировать JSONObject в JsonNode, но гораздо более общий. Будет ли это на самом деле более эффективным или нет, потребуется измерение: либо вы создаете JsonNode в качестве промежуточного, либо TokenBuffer, ни один из способов без накладных расходов.

Смежные вопросы