blob: fe1be2f51ce9ab9b4b9cfc0d450de9f6e12ee639 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.wayang.core.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.SerializationException;
import org.apache.wayang.core.util.json.JSONArray;
import org.apache.wayang.core.util.json.JSONObject;
/**
* Utility to deal with {@link JsonSerializable}s.
*/
public class JsonSerializables {
/**
* Try to serialize the given {@link Object}. It must be JSON-compatible or a {@link JsonSerializable}.
*
* @param obj the {@link Object} to serialize
* @param isPolymorph in case a {@link JSONObject} is created, whether it should be tagged with the {@link Class}
* of {@code obj}
* @return the serialization result
* @see #isJsonCompatible(Object)
*/
public static Object serialize(Object obj, boolean isPolymorph) {
if (obj == null) return null;
if (isJsonCompatible(obj)) {
return obj;
}
if (obj instanceof JsonSerializable) return serialize((JsonSerializable) obj, isPolymorph);
throw new IllegalArgumentException(String.format("Cannot serialize %s.", obj));
}
/**
* Serialize the given {@link JsonSerializable}.
*
* @param serializable the {@link JsonSerializable} to serialize
* @param isPolymorph in case a {@link JSONObject} is created, whether it should be tagged with the {@link Class}
* of {@code serializable}
* @return the serialization result
*/
public static JSONObject serialize(JsonSerializable serializable, boolean isPolymorph) {
return serialize(serializable, isPolymorph, JsonSerializable.uncheckedSerializer);
}
/**
* Serialize the given {@link Object} using a specific {@link JsonSerializer}.
*
* @param obj the {@link Object} to serialize
* @param isPolymorph in case a {@link JSONObject} is created, whether it should be tagged with the {@link Class}
* of {@code serializable}
* @param serializer the {@link JsonSerializer}
* @return the serialization result
*/
public static <T> JSONObject serialize(T obj, boolean isPolymorph, JsonSerializer<T> serializer) {
return addClassTag(obj, serializer.serialize(obj), isPolymorph);
}
/**
* Try to serialize the given {@link Object}s. They must be JSON-compatible or a {@link JsonSerializable}s.
*
* @param collection the {@link Object}s to serialize
* @param isPolymorph in case a {@link JSONObject} is created, whether it should be tagged with the {@link Class}
* of the according {@link Object}
* @return the serialization result
* @see #isJsonCompatible(Object)
*/
public static JSONArray serializeAll(Collection<?> collection, boolean isPolymorph) {
if (isJsonNull(collection)) return null;
JSONArray jsonArray = new JSONArray();
for (Object obj : collection) {
jsonArray.put(serialize(obj, isPolymorph));
}
return jsonArray;
}
/**
* Serialize the given {@link Object}s using a specific {@link JsonSerializer}.
*
* @param collection the {@link Object}s to serialize
* @param isPolymorph in case a {@link JSONObject} is created, whether it should be tagged with the {@link Class}
* of {@code serializable}
* @param serializer the {@link JsonSerializer}
* @return the serialization result
*/
public static <T> JSONArray serializeAll(Collection<T> collection, boolean isPolymorph, JsonSerializer<T> serializer) {
if (collection == null) return null;
JSONArray jsonArray = new JSONArray();
for (T serializable : collection) {
jsonArray.put(serialize(serializable, isPolymorph, serializer));
}
return jsonArray;
}
/**
* Deserialize a given JSON datatype. The following cases are supported:
* <ul>
* <li>{@code json} is a (JSON) {@code null} value;</li>
* <li>{@code json} is a basic (JSON) datatype;</li>
* <li>{@code json} is a {@link Class}-tagged {@link JSONObject} that corresponds to a {@link JsonSerializable};</li>
* <li>{@code json} is a {@link JSONArray} with {@link Class}-tagged {@link JSONObject}s that correspond to a
* {@link JsonSerializable}s - in this case, the result type is a {@link List}.</li>
* </ul>
*
* @param json the JSON data
* @return the deserialization result
*/
public static Object deserialize(Object json) {
if (isJsonNull(json)) return null;
else if (isUnconvertedInstance(json)) return json;
else if (json instanceof JSONObject) return deserialize((JSONObject) json);
else if (json instanceof JSONArray) return deserializeAllAsList((JSONArray) json);
throw new SerializationException(String.format("Don't know how to deserialize %s.", json));
}
/**
* Deserialize a {@link Class}-tagged {@link JSONObject} that should correspond to a {@link JsonSerializable}.
*
* @param jsonObject the {@link JSONObject}
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> T deserialize(JSONObject jsonObject) {
if (isJsonNull(jsonObject)) return null;
return deserialize(jsonObject, JsonSerializable.uncheckedSerializer());
}
/**
* Deserialize a {@link JSONObject} that should correspond to a {@link JsonSerializable}.
*
* @param jsonObject the {@link JSONObject}
* @param cls the {@link Class} of the deserialization product
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> T deserialize(JSONObject jsonObject, Class<? extends T> cls) {
if (isJsonNull(jsonObject)) return null;
return deserialize(jsonObject, (JsonSerializer<T>) JsonSerializable.uncheckedSerializer(), cls);
}
/**
* Deserialize a ({@link Class}-tagged) {@link JSONObject} with a {@link JsonSerializer}.
*
* @param jsonObject the {@link JSONObject}
* @param serializer the {@link JsonSerializer}
* @return the deserialization product
*/
public static <T> T deserialize(JSONObject jsonObject, JsonSerializer<T> serializer) {
if (isJsonNull(jsonObject)) return null;
return serializer.deserialize(jsonObject);
}
/**
* Deserialize a {@link JSONObject} with a {@link JsonSerializer}.
*
* @param jsonObject the {@link JSONObject}
* @param serializer the {@link JsonSerializer}
* @param cls the {@link Class} of the deserialization product
* @return the deserialization product
*/
public static <T> T deserialize(JSONObject jsonObject, JsonSerializer<T> serializer, Class<? extends T> cls) {
if (jsonObject == null || jsonObject.equals(JSONObject.NULL)) return null;
return serializer.deserialize(jsonObject, cls);
}
/**
* Deserialize a {@link JSONArray} according to the rules of {@link #deserialize(Object)}.
*
* @param jsonArray the {@link JSONArray}
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deserializeAllAsList(JSONArray jsonArray) {
if (isJsonNull(jsonArray)) return null;
List<T> result = new ArrayList<>(jsonArray.length());
for (Object jsonElement : jsonArray) {
result.add((T) deserialize(jsonElement));
}
return result;
}
/**
* Deserialize a {@link JSONArray} according to the rules of {@link #deserialize(JSONObject, Class)}.
*
* @param jsonArray the {@link JSONArray}
* @param cls the {@link Class} of the elements in the {@code jsonArray}
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deserializeAllAsList(JSONArray jsonArray, Class<T> cls) {
return deserializeAllAsList(jsonArray, (JsonSerializer<T>) JsonSerializable.uncheckedSerializer(), cls);
}
/**
* Deserialize a {@link JSONArray} according to the rules of {@link #deserialize(JSONObject, JsonSerializer)}.
*
* @param jsonArray the {@link JSONArray}
* @param serializer the {@link JsonSerializer}
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deserializeAllAsList(JSONArray jsonArray, JsonSerializer<T> serializer) {
List<T> result = new ArrayList<>(jsonArray.length());
for (Object jsonElement : jsonArray) {
result.add(isJsonNull(jsonElement) ? null : deserialize((JSONObject) jsonElement, serializer));
}
return result;
}
/**
* Deserialize a {@link JSONArray} according to the rules of {@link #deserialize(JSONObject, JsonSerializer, Class)}.
*
* @param jsonArray the {@link JSONArray}
* @param serializer the {@link JsonSerializer}
* @param cls the {@link Class} of the elements in the {@code jsonArray}
* @return the deserialization product
*/
@SuppressWarnings("unchecked")
public static <T> List<T> deserializeAllAsList(JSONArray jsonArray, JsonSerializer<T> serializer, Class<T> cls) {
List<T> result = new ArrayList<>(jsonArray.length());
for (Object jsonElement : jsonArray) {
result.add(isJsonNull(jsonElement) ? null : deserialize((JSONObject) jsonElement, serializer, cls));
}
return result;
}
/**
* Tag the {@link JSONObject} with the {@link Class} of the other {@link Object}.
*
* @param obj whose {@link Class} should be tagged
* @param jsonObject that should be tagged
* @return the {@code jsonObject}
*/
public static JSONObject addClassTag(Object obj, JSONObject jsonObject) {
if (isJsonNull(jsonObject)) return jsonObject;
return jsonObject.put("_class", obj.getClass().getCanonicalName());
}
/**
* Optionally tag the {@link JSONObject} with the {@link Class} of the other {@link Object}.
*
* @param obj whose {@link Class} should be tagged
* @param jsonObject that should be tagged
* @param isAddClassTag if this is {@code false}, no action will be performed
* @return the {@code jsonObject}
*/
public static JSONObject addClassTag(Object obj, JSONObject jsonObject, boolean isAddClassTag) {
return isAddClassTag ? addClassTag(obj, jsonObject) : jsonObject;
}
/**
* Read and load the specified {@link Class} in a {@link JSONObject}.
*
* @param jsonObject that contains the {@link Class} tag
* @return the loaded {@link Class} or {@code null} if none exists
* @throws ClassNotFoundException if the {@link Class} could not be loaded
*/
public static Class<?> getClassTag(JSONObject jsonObject) throws ClassNotFoundException {
if (isJsonNull(jsonObject) || !jsonObject.has("_class")) return null;
final String className = jsonObject.getString("_class");
return Class.forName(className);
}
/**
* Tells whether the given instance is a (JSON) {@code null} value.
*
* @param obj the instance to test
* @return whether {@code obj} is a (JSON) {@code null} value
*/
public static boolean isJsonNull(Object obj) {
return obj == null || JSONObject.NULL.equals(obj);
}
/**
* Tells whether the given instance is a JSON datatype.
*
* @param obj the instance to test
* @return whether {@code obj} is a JSON datatype
*/
public static boolean isJsonCompatible(Object obj) {
return isUnconvertedInstance(obj) || obj == JSONObject.NULL || obj instanceof JSONObject || obj instanceof JSONArray;
}
/**
* Tells whether the given instance does not require conversion, which is the case for {@link Long}s, {@link Integer}s,
* {@link Double}s, {@link String}s, and {@code null}s.
*
* @param obj the instance to test
* @return whether {@code obj} does not require conversion
*/
public static boolean isUnconvertedInstance(Object obj) {
return obj == null || obj instanceof Long || obj instanceof Integer || obj instanceof Double || obj instanceof String;
}
}