I'd like to convert the an Intent's extras Bundle into a JSONObject so that I can pass it to/from JavaScript.
Is there a quick or best way to do this conversion? It would be alright if not all possible Bundles will work.
You can use Bundle#keySet() to get a list of keys that a Bundle contains. You can then iterate through those keys and add each key-value pair into a JSONObject:
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
try {
// json.put(key, bundle.get(key)); see edit below
json.put(key, JSONObject.wrap(bundle.get(key)));
} catch(JSONException e) {
//Handle exception here
}
}
Note that JSONObject#put will require you to catch a JSONException.
Edit:
It was pointed out that the previous code didn't handle Collection and Map types very well. If you're using API 19 or higher, there's a JSONObject#wrap method that will help if that's important to you. From the docs:
Wrap an object, if necessary. If the object is null, return the NULL
object. If it is an array or collection, wrap it in a JSONArray. If it
is a map, wrap it in a JSONObject. If it is a standard property
(Double, String, et al) then it is already wrapped. Otherwise, if it
comes from one of the java packages, turn it into a string. And if it
doesn't, try to wrap it in a JSONObject. If the wrapping fails, then
null is returned.
private String getJson(final Bundle bundle) {
if (bundle == null) return null;
JSONObject jsonObject = new JSONObject();
for (String key : bundle.keySet()) {
Object obj = bundle.get(key);
try {
jsonObject.put(key, wrap(bundle.get(key)));
} catch (JSONException e) {
e.printStackTrace();
}
}
return jsonObject.toString();
}
public static Object wrap(Object o) {
if (o == null) {
return JSONObject.NULL;
}
if (o instanceof JSONArray || o instanceof JSONObject) {
return o;
}
if (o.equals(JSONObject.NULL)) {
return o;
}
try {
if (o instanceof Collection) {
return new JSONArray((Collection) o);
} else if (o.getClass().isArray()) {
return toJSONArray(o);
}
if (o instanceof Map) {
return new JSONObject((Map) o);
}
if (o instanceof Boolean ||
o instanceof Byte ||
o instanceof Character ||
o instanceof Double ||
o instanceof Float ||
o instanceof Integer ||
o instanceof Long ||
o instanceof Short ||
o instanceof String) {
return o;
}
if (o.getClass().getPackage().getName().startsWith("java.")) {
return o.toString();
}
} catch (Exception ignored) {
}
return null;
}
public static JSONArray toJSONArray(Object array) throws JSONException {
JSONArray result = new JSONArray();
if (!array.getClass().isArray()) {
throw new JSONException("Not a primitive array: " + array.getClass());
}
final int length = Array.getLength(array);
for (int i = 0; i < length; ++i) {
result.put(wrap(Array.get(array, i)));
}
return result;
}
Here is a Gson type adapter factory that converts a Bundle to JSON.
https://github.com/google-gson/typeadapters/blob/master/android/src/main/java/BundleTypeAdapterFactory.java
If the bundle has nested bundles then JSONObject.wrap(bundle.get(key)) will return null. So I managed to get it to work for my use case with this recursive function. Haven't tested more advanced use cases though.
JSONObject json = convertBundleToJson(bundle);
public JSONObject convertBundleToJson(Bundle bundle) {
JSONObject json = new JSONObject();
Set<String> keys = bundle.keySet();
for (String key : keys) {
try {
if (bundle.get(key) != null && bundle.get(key).getClass().getName().equals("android.os.Bundle")) {
Bundle nestedBundle = (Bundle) bundle.get(key);
json.put(key, convertToJson(nestedBundle));
} else {
json.put(key, JSONObject.wrap(bundle.get(key)));
}
} catch(JSONException e) {
System.out.println(e.toString());
}
}
return json;
}
Object myJsonObj = bundleObject.get("yourKey");
JsonParser parser = new JsonParser();
JsonObject json = parser.parse(myJsonObj.toString()).getAsJsonObject();
json.get("memberInJson").getAsString();
private static void createFlatJSon(Bundle appRestrictions, JSONObject jsonObject) throws JSONException{
for (String key : appRestrictions.keySet()) {
if (appRestrictions.get(key) instanceof Bundle) {
Bundle bundle = (Bundle)appRestrictions.get(key);
Map<String, String> map = ((Bundle)appRestrictions.get(key)).keySet().stream().collect(Collectors.toMap(x -> x, x -> bundle.get(x).toString()));
JSONObject jsonNested = new JSONObject(map);
jsonObject.put(key,jsonNested);
//createFlatJSon((Bundle) appRestrictions.get(key),jsonObject);
}else if (appRestrictions.get(key) instanceof Parcelable[]){
for (int i=0;i< ((Parcelable[]) appRestrictions.get(key)).length; i++){
createFlatJSon((Bundle)((Parcelable[]) appRestrictions.get(key))[i],jsonObject);
}
//Log.e("KEY skipped",appRestrictions.get(key).toString());
}else{
// map = appRestrictions.keySet().stream().collect(Collectors.toMap(x -> x, x -> appRestrictions.get(x).toString()));// Use this if don't want to modify the keys
Log.e("KEY: ", key + " Value:" + appRestrictions.getString(key));
Log.e("KEY: ", key + " Value:" + appRestrictions.get(key).getClass().getSimpleName());
if (appRestrictions.get(key) instanceof String[]){
JSONArray jsonArray = new JSONArray();
for (String value : (String[])appRestrictions.get(key)) {
jsonArray.put(value);
}
jsonObject.put(key,jsonArray);
}else {
jsonObject.put(key, appRestrictions.get(key).toString());
}
}
}
}
Related
I want to convert dot notated string to a JSONObject but include arrays too, for example: I want to set first.second[0].third[0].fourth to some string. So, JSON must be:
{
"first": {
"second": [
{
"third": [
"fourth": "some string"
]
}
]
}
}
I found this method then edited and it turned out something like this:
private void expand(Object parent, String key, String value) {
if (key == null) return;
if (!key.contains(".") && !key.contains("[")) {
if (parent instanceof JSONObject) {
((JSONObject) parent).put(key, value);
} else {
((JSONArray) parent).put(value);
}
return;
}
String innerKey = key.substring(0, key.contains(".") ? key.indexOf(".") : key.length());
String formattedInnerKey = innerKey.contains("[") ? innerKey.substring(0, innerKey.indexOf("[")) : innerKey;
String remaining = key.contains(".") ? key.substring(key.indexOf(".") + 1) : key.contains("]") ? key.substring(key.indexOf("]") + 1) : null;
if (parent instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) parent;
if (jsonObject.has(formattedInnerKey)) {
expand(jsonObject.get(formattedInnerKey), remaining, value);
return;
}
} else {
JSONArray jsonArray = (JSONArray) parent;
Matcher matcher = Pattern.compile("(?<=\\[)([^\\]]+)(?=\\])").matcher(innerKey);
Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find a index number in \"%s\"", innerKey));
int index = Integer.parseInt(matcher.group());
System.out.print(index + " - ");
if (!jsonArray.isNull(index)) {
System.out.print(jsonArray.get(index));
expand(jsonArray.get(index), remaining, value);
return;
}
}
Object obj = innerKey.contains("[") ? new JSONArray() : new JSONObject();
if (parent instanceof JSONObject) {
((JSONObject) parent).put(formattedInnerKey, obj);
} else {
JSONObject base = new JSONObject();
base.put(formattedInnerKey, obj);
Matcher matcher = Pattern.compile("(?<=\\[)([^\\]]+)(?=\\])").matcher(innerKey);
Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find a index number in \"%s\"", innerKey));
int index = Integer.parseInt(matcher.group());
((JSONArray) parent).put(index, base);
}
expand(obj, remaining, value);
}
This method -kinda- works but the problem is that it adds elements to the array instead of putting. I want to be able to put the object to an index in that array. How can I fix this?
Here's the solution:
public void expand(Object parent, String key, Object value) {
JSONElement element = new JSONElement(parent);
if (!key.contains(".")) { // End
element.put(key, value);
return;
}
String innerKey = key.substring(0, key.indexOf("."));
String remaining = key.substring(key.indexOf(".") + 1);
if (element.has(innerKey)) {
expand(element.get(innerKey), remaining, value);
return;
}
Object object = element.newInstance();
Object put = element.put(innerKey, object);
expand(put, remaining, value);
}
JSONElement class:
import com.google.common.base.Preconditions;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JSONElement {
private static final Pattern pattern = Pattern.compile("(?<=\\[)([^]]+)(?=])");
private final Object base;
public JSONElement(Object base) {
Preconditions.checkNotNull(base, "base");
Preconditions.checkState(base instanceof JSONObject || base instanceof JSONArray, "base must be a JSONObject or JSONArray instead of " + base.getClass().getSimpleName());
this.base = base;
}
public Object put(Object key, Object value) {
String keyAsString = String.valueOf(key);
if (base instanceof JSONObject) {
if (keyAsString.contains("[")) {
String formatKey = keyAsString.contains("[") ? keyAsString.substring(0, keyAsString.indexOf("[")) : keyAsString;
JSONArray array = ((JSONObject) base).has(formatKey) ? ((JSONObject) base).getJSONArray(formatKey) : new JSONArray();
int index = getIndex(keyAsString);
array.put(index, value);
((JSONObject) base).put(formatKey, array);
return ((JSONArray) ((JSONObject) base).get(formatKey)).get(index);
}
((JSONObject) base).put(keyAsString, value);
return ((JSONObject) base).get(keyAsString);
}
int index = getIndex(keyAsString);
((JSONArray) base).put(index, value);
return ((JSONArray) base).get(index);
}
public boolean has(Object key) {
String keyAsString = String.valueOf(key);
if (base instanceof JSONObject) {
JSONObject object = (JSONObject) base;
String formatKey = formatKey(keyAsString);
if (keyAsString.contains("["))
return object.has(formatKey) && !object.getJSONArray(formatKey).isNull(getIndex(keyAsString));
return object.has(formatKey);
}
return !((JSONArray) base).isNull(getIndex(keyAsString));
}
public Object get(Object key) {
String keyAsString = String.valueOf(key);
if (base instanceof JSONObject) {
JSONObject object = (JSONObject) base;
String formatKey = formatKey(keyAsString);
if (keyAsString.contains("["))
return object.getJSONArray(formatKey).get(getIndex(keyAsString));
return object.get(formatKey);
}
return ((JSONArray) base).get(getIndex(keyAsString));
}
public Object newInstance() {
return base instanceof JSONObject ? new JSONObject() : new JSONArray();
}
private int getIndex(String key) {
Matcher matcher = pattern.matcher(key);
Preconditions.checkState(matcher.find(), String.format("Matcher couldn't find an index number in \"%s\"", key));
return Integer.parseInt(matcher.group());
}
private String formatKey(String key) {
return key.contains("[") ? key.substring(0, key.indexOf("[")) : key;
}
}
Usage:
JSONObject jsonObject = new JSONObject();
expand(jsonObject, "first.second[0].third[0].fourth", "some string");
System.out.println(jsonObject.toString()); // Prints: {"first":{"second":[{"third":[{"fourth":"some string"}]}]}}
Issue Description
Hi I'm try to extract object in dynamic nested json response ,below method works without return but I need get object value to store it as array list for later usage now I getting null value please help me to get solve thanks in advance
public static Object getKey(JSONObject jsonObject, String key) {
boolean exists = jsonObject.has(key);
Iterator<String> keys;
String nextKeys;
Object value = null;
if (!exists) {
keys = jsonObject.keys();
while (keys.hasNext()) {
nextKeys = keys.next();
try {
if (jsonObject.get(nextKeys) instanceof JSONObject) {
if (exists == false) {
getKey(jsonObject.getJSONObject(nextKeys), key);
}
} else if (jsonObject.get(nextKeys) instanceof JSONArray) {
JSONArray jsonArray = jsonObject.getJSONArray(nextKeys);
for (int i = 0; i < jsonArray.length(); i++) {
String jsonArrayString = jsonArray.get(i).toString();
JSONObject innerJson = new JSONObject(jsonArrayString);
if (exists == false) {
getKey(innerJson, key);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
} else if (exists) {
value = jsonObject.get(key);
} else {
System.out.println("Not available");
}
return value;
}
I wrote the following code.
String body = IOUtils.toString(requestWrapper.getReader());
JSONParser parser = new JSONParser();
Object object = parser.parse(body);
object = clean(object);
body = object.toString();
requestWrapper.resetInputStream(body.getBytes());
private Object clean(Object object) {
if (object instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) object;
for (int i = 0; i < jsonArray.size(); i++) {
jsonArray.set(i, clean(jsonArray.get(i)));
}
return jsonArray;
} else if (object instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) object;
for (Object o : jsonObject.keySet()) {
jsonObject.put(o, clean(((JSONObject) object).get(o)));
}
return jsonObject;
} else if (Objects.nonNull(object)){
return object.toString().replace("<", "<").replace(">", ">");
} else {
return null;
}
}
This seems to be working normally. But I don't know if this is right.
Implementing code like this is likely to cause data loss.
I have a JsonObject that contains several JsonArrays:
{
"key1" : [[1, 2, 3, 4]],
"key2" : [[1, 2, 3, 4]]
}
I would like to pack it using MessagePack without using the JsonObjects string representation,
So far i haven't found any documentation as to how to accomplish this,
Any ideas?
I have written a recursive code in order to achieve this,
Here is the code if anyone else has encountered this issue:
public byte[] convert(JSONObject data) throws IOException, JSONException {
MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
packJObject(packer, data);
packer.close();
return packer.toByteArray();
}
private void packJObject(MessageBufferPacker packer, JSONObject data) throws IOException, JSONException {
packer.packMapHeader(data.length());
Iterator<String> keys = data.keys();
while(keys.hasNext()) {
String key = keys.next();
packer.packString(key); // pack the key
Object value = data.get(key);
if(value instanceof JSONArray) {
packJArray(packer, (JSONArray)value);
}
else if(value instanceof JSONObject) {
packJObject(packer, (JSONObject) value);
}
else {
packPrimitive(packer, value);
}
}
}
private void packJArray(MessageBufferPacker packer, JSONArray data) throws IOException, JSONException {
packer.packArrayHeader(data.length());
for(int i = 0; i < data.length(); i++) {
Object value = data.get(i);
if(value instanceof JSONObject) {
packJObject(packer,(JSONObject)value);
}
else if(value instanceof JSONArray){
packJArray(packer,(JSONArray)value);
}
else {
packPrimitive(packer, value);
}
}
}
private void packPrimitive(MessageBufferPacker packer, Object value) throws IOException {
if(value instanceof String) {
packer.packString((String)value);
}
else if(value instanceof Integer) {
packer.packInt((Integer) value);
}
else if(value instanceof Boolean) {
packer.packBoolean((boolean)value);
}
else if(value instanceof Double) {
packer.packDouble((double)value);
}
else if(value instanceof Long) {
packer.packLong((long)value);
}
else {
throw new IOException("Invalid packing value of type " + value.getClass().getName());
}
}
I would like to mask certain elements of JSON and print to logs. Masking can be either by substituting by dummy data or removing the key pair .Is there a utility to do the masking in Java ?
E.g.,
given JSON:
{
"key1":"value1",
"key2":"value2",
"key3":"value3",
}
mask key 2 alone and print JSON:
{
"key1":"value1",
"key2":"xxxxxx",
"key3":"value3",
}
or
{
"key1":"value1",
"key3":"value3",
}
input will be JSON object or array type in string format. Here the maskable keys only static otherwise input string will dynamic.
public final class MaskPIData {
/**
* Mask able keywords mentioned here. It should be in LOWER CASE.
*/
private static final Set<String> MASKABLE_KEYS = new HashSet<>(Arrays.asList(
"email",
"emails",
"phone",
"pin",
"password",
"phonenumber",
"moneys"));
private static final String MASKING_VALUE = "****";
private static final ObjectMapper OBJECTMAPPER = new ObjectMapper();
private MaskPIData() {
super();
}
private static boolean isValidSet(Set<String> set) {
return set != null && !set.isEmpty();
}
private static boolean isKnownPrimitiveWrapperModel(Object obj) {
return obj == null || obj instanceof String || obj instanceof Integer || obj instanceof Long
|| obj instanceof Double;
}
#SuppressWarnings("unchecked")
private static JSONObject maskingForJsonObject(Set<String> maskableKeys, JSONObject input) {
if (!isValidSet(maskableKeys) || input == null) {
return input;
}
Map<String, Object> inputMap = (Map<String, Object>) input;
Map<String, Object> caseInsensitiveInputMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
caseInsensitiveInputMap.putAll(inputMap);
for (Map.Entry<String, Object> entryPair : caseInsensitiveInputMap.entrySet()) {
if (entryPair.getValue() instanceof JSONArray) {
JSONArray jsonArr = (JSONArray) caseInsensitiveInputMap.get(entryPair.getKey());
maskingForArray(maskableKeys, entryPair.getKey(), jsonArr);
caseInsensitiveInputMap.put(entryPair.getKey(), jsonArr);
} else if (entryPair.getValue() instanceof JSONObject) {
JSONObject jsonObj = (JSONObject) caseInsensitiveInputMap.get(entryPair.getKey());
caseInsensitiveInputMap.put(entryPair.getKey(), maskingForJsonObject(maskableKeys, jsonObj));
} else if (entryPair.getKey() != null && maskableKeys.contains(entryPair.getKey().toLowerCase())) {
caseInsensitiveInputMap.put(entryPair.getKey(), MASKING_VALUE);
}
}
return OBJECTMAPPER.convertValue(caseInsensitiveInputMap, JSONObject.class);
}
#SuppressWarnings("unchecked")
private static JSONArray maskingForArray(Set<String> maskableKeys, String key,
JSONArray jsonArr) {
JSONArray toRet = jsonArr;
for (int idx = 0; idx < toRet.size(); idx++) {
Object obj = toRet.get(idx);
if (isKnownPrimitiveWrapperModel(obj)) {
if (key != null && maskableKeys.contains(key.toLowerCase())) {
toRet.remove(idx);
toRet.add(idx, MASKING_VALUE);
}
} else {
JSONObject jsonObjFromArray = (JSONObject) toRet.get(idx);
JSONObject maskedJsonObj = maskingForJsonObject(maskableKeys, jsonObjFromArray);
toRet.remove(idx);
toRet.add(idx, maskedJsonObj);
}
}
return toRet;
}
public static String doMask(String input) {
String maskedData = input;
if (maskedData != null && !maskedData.trim().isEmpty()) {
try {
if (new JSONParser().parse(maskedData) instanceof JSONObject) {
JSONObject maskedOutput = maskingForJsonObject(MASKABLE_KEYS,
(JSONObject) new JSONParser().parse(maskedData));
maskedData = OBJECTMAPPER.writeValueAsString(maskedOutput);
} else if (new JSONParser().parse(maskedData) instanceof JSONArray) {
JSONArray maskedOutput = maskingForArray(MASKABLE_KEYS, null, (JSONArray) new JSONParser().parse(maskedData));
maskedData = OBJECTMAPPER.writeValueAsString(maskedOutput);
}
} catch (Exception e) {
// to do - Error while masking data
}
}
return maskedData;
}
public static void main(String args[]) {
String input = "{\"item\":{\"test\":\"test\",\"phone\":\"993244\",\"email\":\"mail#mail.com\"}}";
System.out.println(doMask(input));
}
You could use jackson to convert json to map, process map and convert map back to json.
For example:
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
public void mask() throws IOException {
String jsonString = "{\n" +
" \"key1\":\"value1\",\n" +
" \"key2\":\"value2\",\n" +
" \"key3\":\"value3\"\n" +
"}";
Map<String, Object> map;
// Convert json to map
ObjectMapper mapper = new ObjectMapper();
try {
TypeReference ref = new TypeReference<Map<String, Object>>() { };
map = mapper.readValue(jsonString, ref);
} catch (IOException e) {
System.out.print("cannot create Map from json" + e.getMessage());
throw e;
}
// Process map
if(map.containsKey("key2")) {
map.put("key2","xxxxxxxxx");
}
// Convert back map to json
String jsonResult = "";
try {
jsonResult = mapper.writeValueAsString(map);
} catch (IOException e) {
System.out.print("cannot create json from Map" + e.getMessage());
}
System.out.print(jsonResult);