/*
* Copyright 2012 Nodeable Inc
*
* Licensed 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 com.streamreduce.util;
import com.google.common.collect.Maps;
import net.sf.json.JSON;
import net.sf.json.JSONArray;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import net.sf.json.xml.XMLSerializer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.bson.types.ObjectId;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class JSONUtils {
/**
* Takes an XML string and converts it to a JSONObject.
*
* @param xml the XML string
*
* @return a JSONObject
*/
public static JSONObject xmlToJSON(String xml) {
return ((JSONObject)new XMLSerializer().read(xml));
}
/**
* Flattens JSON into a single level of key/value pairs.
*
* @param json the {@link JSON} object to flatten
* @param prefix the prefix to append to each key
*
* @return the newly flattened {@link JSONObject}
*/
public static JSONObject flattenJSON(JSON json, String prefix) {
JSONObject flattenedJSON = new JSONObject();
prefix = (StringUtils.isNotBlank(prefix) ? prefix + "." : "");
if (json instanceof JSONArray) {
JSONArray jsonArray = (JSONArray)json;
for (int i = 0; i < jsonArray.size(); i++) {
Object value = jsonArray.get(i);
String flatKey = prefix + i;
if (value instanceof JSONArray) {
flattenedJSON.putAll(flattenJSON((JSONArray)value,flatKey));
} else if (value instanceof JSONObject) {
flattenedJSON.putAll(flattenJSON((JSONObject)value, flatKey));
} else {
flattenedJSON.put(flatKey, value);
}
}
} else if (json instanceof JSONObject) {
JSONObject jsonObject = (JSONObject)json;
for (Object rawKey : jsonObject.keySet()) {
String key = rawKey.toString();
Object value = jsonObject.get(key);
String flatKey = prefix + key;
if (value instanceof JSONArray) {
flattenedJSON.putAll(flattenJSON((JSONArray)value, flatKey));
} else if (value instanceof JSONObject) {
flattenedJSON.putAll(flattenJSON((JSONObject)value, flatKey));
} else {
flattenedJSON.put(flatKey, value);
}
}
} else {
throw new IllegalArgumentException("Unable to flatten a JSONNull.");
}
return flattenedJSON;
}
/**
* Reads in a JSON file from the classpath.
*
* @param resource the resource to read in as a string
*
* @return the contents of the resource as string
*
* @throws Exception if anything goes wrong
*/
public static String readJSONFromClasspath(String resource) throws Exception {
InputStream inputStream = JSONUtils.class.getResourceAsStream(resource);
StringWriter writer = new StringWriter();
IOUtils.copy(inputStream, writer, "UTF-8");
return writer.toString();
}
/**
* Copies and sanitizes any Map so that it is suitable for conversion to JSON. This method only converts any
* (nested) values that are of type ObjectId to its .toString() value.
*
* @param map Any Map.
* @return A mutable Map<Object,Object> copied from the passed in Map
* with sanitized values specific to Nodeable for converting to JSON.
*/
public static <T> Map<T, Object> sanitizeMapForJson(Map<T,Object> map) {
Map<T,Object> returnMap = Maps.newHashMap(map);
for (T key : returnMap.keySet()) {
Object o = returnMap.get(key);
if (o instanceof Map) {
returnMap.put(key, sanitizeMapForJson((Map<Object,Object>)o));
}
if (o instanceof ObjectId) {
returnMap.put(key,o.toString());
}
}
return returnMap;
}
@SuppressWarnings("unchecked")
public static <T> Map<T,Object> replaceJSONNullsFromMap(Map<T,Object> oldMap) {
if (oldMap == null) { return null;}
Map<T,Object> map = new HashMap<>(oldMap);
//Iterate over old keyset since we modify the new map
for (T key : oldMap.keySet()) {
Object value = oldMap.get(key);
if (JSONNull.getInstance().equals(value)) {
map.put(key,null);
} else if (value instanceof Map) {
map.put(key, replaceJSONNullsFromMap((Map<T, Object>) value));
} else if (value instanceof List) {
map.put(key, replaceJSONNullsFromList((List<Object>) value));
}
}
return map;
}
@SuppressWarnings("unchecked")
public static List<Object> replaceJSONNullsFromList(List<Object> list) {
if (list == null) { return null; }
List<Object> newList = new ArrayList<>(list);
for (int i = 0; i < newList.size() ; i++) {
Object o = newList.get(i);
if (o instanceof Map) {
newList.set(i, replaceJSONNullsFromMap((Map) o));
} else if (o instanceof List) {
newList.set(i, replaceJSONNullsFromList((List<Object>) o));
} else if (JSONNull.getInstance().equals(o)) {
newList.set(i,null);
}
}
return newList;
}
public static Map<String,Object> convertJSONObjectToMap(JSONObject jsonObject) {
if (jsonObject == null) { return null;}
Map<String,Object> map = new HashMap<>();
for (Object key : jsonObject.keySet()) {
map.put(key.toString(),jsonObject.get(key));
}
return replaceJSONNullsFromMap(map);
}
}