Solución:
Dado que tiene una representación abstracta de algunos datos JSON (un org.json.JSONObject
objeto) y planea usar la biblioteca Jackson, que tiene su propia representación abstracta de datos JSON (com.fasterxml.jackson.databind.JsonNode
) – entonces una conversión de una representación a otra lo salvaría del proceso parse-serialize-parse. Entonces, en lugar de usar el readValue
método que acepta un String
, usaría esta versión que acepta JsonParser
:
JSONObject jsonObject = //...
JsonNode jsonNode = convertJsonFormat(jsonObject);
ObjectMapper mapper = new ObjectMapper();
MyPojoClass myPojo = mapper.readValue(new TreeTraversingParser(jsonNode), MyPojoClass.class);
JSON es un formato muy simple, por lo que no debería ser difícil crear el convertJsonFormat
manualmente. Aquí está mi intento:
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;
}
Tenga en cuenta que, mientras que los Jackson JsonNode
puede representar algunos tipos adicionales (como BigInteger
, Decimal
, etc.) no son necesarios ya que el código anterior cubre todo lo que JSONObject
puede representar.
Si no está vinculado con Jackson, puede utilizar la práctica biblioteca de google-gson como alternativa. Solo requiere un frasco y es muy simple de usar:
Conversión de un objeto java en una cadena JSON:
String json_string = new Gson().toJson(an_object);
Creando un objeto java a partir de una cadena JSON:
MyObject obj = new Gson().fromJson(a_json_string, MyObject.class);
No sé sobre rendimiento en comparación con Jackson, pero es difícil ser más simple que esto … Gson es una biblioteca estable y ampliamente utilizada.
Ver https://code.google.com/p/google-gson/
Añadiendo una respuesta a una pregunta anterior, pero …
Jackson puede enlazar a / desde los tipos org.json. En general, puede convertir entre cualquier tipo al que pueda enlazar, por efectivamente (aunque no en realidad) serializando a JSON y deserializando.
Si tiene JsonOrgModule registrado, simplemente puede hacer la conversión directamente desde 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;
}
Mencioné que esto es efectivamente serializando? Eso es cierto, serializa el objeto de entrada en un TokenBuffer, que representa un flujo de eventos de análisis JSON, pero con menos impacto en la construcción de cadenas, etc., ya que puede hacer referencia en gran medida a datos de la entrada. A continuación, envía esta secuencia a un deserializador para producir el objeto de salida.
Entonces, es algo similar a la sugerencia de convertir JSONObject en un JsonNode, pero mucho más general. Si es realmente más eficiente o no, necesitaría medir: o construye un JsonNode como intermedio o un TokenBuffer, ninguna de las dos formas está sin gastos generales.