5

I'm using the com.google.gson.Gson API to convert a JSON string to a Java object:

Gson gson = new Gson();
User u = gson.fromJson(jsonString, User.class);

I'd like to know if there's an equivalent API in JSON-P (javax.json) API to do the equivalent. I'm using Java EE 7. Thanks!

2
  • It's possible, but very definitely not with an oneliner. You'll need help of Java Beans Introspector API. Would this be acceptable? Commented Jun 15, 2015 at 12:55
  • Yes, that would be acceptable (using only JSON API calls). Commented Jun 16, 2015 at 8:53

3 Answers 3

9
+50

This is in JSON-P (javax.json) definitely not possible with an oneliner like as with Gson. The JSON-P API as available in Java EE 7 is very low level. It only returns you a JSON object structure which you've to breakdown and map to a javabean further all by yourself.

In order to achieve (nearly) the same effect as Gson#fromJson(), here's a kickoff example of the boilerplate code. Parameterized types and javabeans are supported to certain extent. It's all standard Java SE API, the javabeans are introspected with little help of java.beans API.

@SuppressWarnings("unchecked")
public static <T> T fromJson(String json, Class<T> beanClass) {
    JsonValue value = Json.createReader(new StringReader(json)).read();
    return (T) decode(value, beanClass);
}

private static Object decode(JsonValue jsonValue, Type targetType) {
    if (jsonValue.getValueType() == ValueType.NULL) {
        return null;
    }
    else if (jsonValue.getValueType() == ValueType.TRUE || jsonValue.getValueType() == ValueType.FALSE) {
        return decodeBoolean(jsonValue, targetType);
    }
    else if (jsonValue instanceof JsonNumber) {
        return decodeNumber((JsonNumber) jsonValue, targetType);
    }
    else if (jsonValue instanceof JsonString) {
        return decodeString((JsonString) jsonValue, targetType);
    }
    else if (jsonValue instanceof JsonArray) {
        return decodeArray((JsonArray) jsonValue, targetType);
    }
    else if (jsonValue instanceof JsonObject) {
        return decodeObject((JsonObject) jsonValue, targetType);
    }
    else {
        throw new UnsupportedOperationException("Unsupported json value: " + jsonValue);
    }
}

private static Object decodeBoolean(JsonValue jsonValue, Type targetType) {
    if (targetType == boolean.class || targetType == Boolean.class) {
        return Boolean.valueOf(jsonValue.toString());
    }
    else {
        throw new UnsupportedOperationException("Unsupported boolean type: " + targetType);
    }
}

private static Object decodeNumber(JsonNumber jsonNumber, Type targetType) {
    if (targetType == int.class || targetType == Integer.class) {
        return jsonNumber.intValue();
    }
    else if (targetType == long.class || targetType == Long.class) {
        return jsonNumber.longValue();
    }
    else {
        throw new UnsupportedOperationException("Unsupported number type: " + targetType);
    }
}

private static Object decodeString(JsonString jsonString, Type targetType) {
    if (targetType == String.class) {
        return jsonString.getString();
    }
    else if (targetType == Date.class) {
        try {
            return new SimpleDateFormat("MMM dd, yyyy H:mm:ss a", Locale.ENGLISH).parse(jsonString.getString()); // This is default Gson format. Alter if necessary.
        }
        catch (ParseException e) {
            throw new UnsupportedOperationException("Unsupported date format: " + jsonString.getString());
        }
    }
    else {
        throw new UnsupportedOperationException("Unsupported string type: " + targetType);
    }
}

private static Object decodeArray(JsonArray jsonArray, Type targetType) {
    Class<?> targetClass = (Class<?>) ((targetType instanceof ParameterizedType) ? ((ParameterizedType) targetType).getRawType() : targetType);

    if (List.class.isAssignableFrom(targetClass)) {
        Class<?> elementClass = (Class<?>) ((ParameterizedType) targetType).getActualTypeArguments()[0];
        List<Object> list = new ArrayList<>();

        for (JsonValue item : jsonArray) {
            list.add(decode(item, elementClass));
        }

        return list;
    }
    else if (targetClass.isArray()) {
        Class<?> elementClass = targetClass.getComponentType();
        Object array = Array.newInstance(elementClass, jsonArray.size());

        for (int i = 0; i < jsonArray.size(); i++) {
            Array.set(array, i, decode(jsonArray.get(i), elementClass));
        }

        return array;
    }
    else {
        throw new UnsupportedOperationException("Unsupported array type: " + targetClass);
    }
}

private static Object decodeObject(JsonObject object, Type targetType) {
    Class<?> targetClass = (Class<?>) ((targetType instanceof ParameterizedType) ? ((ParameterizedType) targetType).getRawType() : targetType);

    if (Map.class.isAssignableFrom(targetClass)) {
        Class<?> valueClass = (Class<?>) ((ParameterizedType) targetType).getActualTypeArguments()[1];
        Map<String, Object> map = new LinkedHashMap<>();

        for (Entry<String, JsonValue> entry : object.entrySet()) {
            map.put(entry.getKey(), decode(entry.getValue(), valueClass));
        }

        return map;
    }
    else try {
        Object bean = targetClass.newInstance();

        for (PropertyDescriptor property : Introspector.getBeanInfo(targetClass).getPropertyDescriptors()) {
            if (property.getWriteMethod() != null && object.containsKey(property.getName())) {
                property.getWriteMethod().invoke(bean, decode(object.get(property.getName()), property.getWriteMethod().getGenericParameterTypes()[0]));
            }
        }

        return bean;
    }
    catch (Exception e) {
        throw new UnsupportedOperationException("Unsupported object type: " + targetClass, e);
    }
}

Usage:

User u = YourJsonUtil.fromJson(jsonString, User.class);

In case you're seeing an UnsupportedOperationException coming from one of decodeXxx() methods, just add the desired conversion logic to the method in question. Of course this could be refactored further to apply the strategy pattern and such so that it's flexible and extensible, but then we're basically reinventing Gson.

In Java/Jakarta EE 8 a new API will be introduced, the JSON-B (jakarta.json.bind) API, which should remove much of this boilerplate:

Jsonb jsonb = JsonbBuilder.create();
User u = jsonb.fromJson(jsonString, User.class);

You can find more information and examples in the JSON-B specification.

Sign up to request clarification or add additional context in comments.

Comments

2

No, there is not.

And before you try other parsers then GSON: org.json is buggy/has implementation mistakes. QuickJson is buggy/has implementation mistakes

Comments

0

In Jakarta EE 8 or later, JSON-P is used for low level JSON value operations, and JSON-B is used for JSON binding.

For your case, use JSONB is preferred. see Jsonb Javadoc: https://jakarta.ee/specifications/platform/10/apidocs/jakarta/json/bind/jsonb

 Jsonb jsonb = JsonbBuilder.create();
 User user = jsonb.fromJson("<json string>", User.class);

Note: Jakarta EE 8 renamed Maven coordinates from javax to jakarta, and Jakarta EE 9 renamed the packages from javax to jakarta.

1 Comment

Indeed. But noted should be that JSON-B didn't exist in Java EE 7. It was only introduced in Java EE 8, which was released more than 2 years after the current question was asked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.