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());
}
}
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 am trying to extra value for avatar from all different node. My json looks like this
{
"page":1,
"per_page":3,
"total":12,
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg",
"total_pages":4,
"data":[
{
"id":1,
"first_name":"George",
"last_name":"Bluth",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
},
{
"id":2,
"first_name":"Janet",
"last_name":"Weaver",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
},
{
"id":3,
"first_name":"Emma",
"last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
}
],
"user":{
"id":3,
"first_name":"Emma",
"last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
}
}
I have tried following solution
public void getAllParentChildNodeAsMap(JSONObject jObject, Map<String, Object> result) throws JSONException {
Iterator<String> iterator = jObject.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
Object value = null;
if (jObject.getJSONObject(key) instanceof JSONObject) {
JSONObject jsonValue = jObject.getJSONObject(key);
getAllParentChildNodeAsMap(jsonValue, result);
} else {
value = jObject.get(key);
}
if(key.equals("avatar")) {
result.put(key, value);
}
}
log.info(result.values().toString());
}
And it keeping giving me following error
org.json.JSONException: JSONObject["per_page"] is not a JSONObject.
at org.json.JSONObject.getJSONObject(JSONObject.java:782)
at com.rnd.restapi.serenity.steps.CommonFacilities.getAllParentChildNodeAsMap(CommonFacilities.java:72)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f.CGLIB$getAllParentChildNodeAsMap$5(<generated>)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f$$FastClassByCGLIB$$bb25cc09.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at net.thucydides.core.steps.BaseMethodRunner.invokeMethod(BaseMethodRunner.java:10)
at net.thucydides.core.steps.NormalMethodRunner.invokeMethodAndNotifyFailures(NormalMethodRunner.java:20)
at net.thucydides.core.steps.StepInterceptor.runNormalMethod(StepInterceptor.java:390)
at net.thucydides.core.steps.StepInterceptor.testStepResult(StepInterceptor.java:161)
at net.thucydides.core.steps.StepInterceptor.intercept(StepInterceptor.java:72)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f.getAllParentChildNodeAsMap(<generated>)
at com.rnd.restapi.serenity.steps.CommonFacilities.getAllParentChildNode(CommonFacilities.java:64)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f.CGLIB$getAllParentChildNode$4(<generated>)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f$$FastClassByCGLIB$$bb25cc09.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
at net.thucydides.core.steps.StepInterceptor.invokeMethod(StepInterceptor.java:478)
at net.thucydides.core.steps.StepInterceptor.executeTestStepMethod(StepInterceptor.java:463)
at net.thucydides.core.steps.StepInterceptor.runTestStep(StepInterceptor.java:438)
at net.thucydides.core.steps.StepInterceptor.runOrSkipMethod(StepInterceptor.java:179)
at net.thucydides.core.steps.StepInterceptor.testStepResult(StepInterceptor.java:166)
at net.thucydides.core.steps.StepInterceptor.intercept(StepInterceptor.java:72)
at com.rnd.restapi.serenity.steps.CommonFacilities$$EnhancerByCGLIB$$d9a1a28f.getAllParentChildNode(<generated>)
at com.rnd.restapi.tests.CountriesSearchTests.get_service_is_successful_and_status_code(CountriesSearchTests.java:32)
at ✽.Get service is successful and status code 200(src/test/resource/feature/get.feature:7)
You should use optJSONObject method if you are not sure what kind of JSON node it is behind given key. getJSONObject method throws org.json.JSONException exception when given value is not JSONObject. Below you can find fixed method implementation:
void getAllParentChildNodeAsMap(JSONObject jObject, List<String> result) {
for (String key : jObject.keySet()) {
if (key.equals("avatar")) {
result.add(jObject.getString(key));
continue;
}
JSONObject value = jObject.optJSONObject(key);
if (value != null) {
getAllParentChildNodeAsMap(value, result);
continue;
}
JSONArray array = jObject.optJSONArray(key);
if (array != null) {
for (int i = 0; i < array.length(); i++) {
JSONObject item = array.optJSONObject(i);
if (item != null) {
getAllParentChildNodeAsMap(item, result);
}
}
}
}
}
It is much useful to return List with avatar URL-es because for each URL avatar is a key.
I have a function with a line that is supposedly meant to prevent a concurrent modification exception
List parseObjectKeys = new ArrayList<>(parseObject.keySet());
However it still happens every now and again
private static void convertParseObject(ParseObject parseObject,
HashMap<String, HashMap<String, WritableMap>> topLevel,
ArrayList<Task<Void>> tasks) {
if (parseObject != null) {
String className = parseObject.getClassName();
String id = parseObject.getObjectId();
if (!topLevel.containsKey(className)) {
topLevel.put(className, new HashMap<String, WritableMap>());
}
if (!topLevel.get(className).containsKey(id)) {
final WritableMap flatMap = Arguments.createMap();
flatMap.putString("class", className);
flatMap.putString("id", id);
if (parseObject.isDataAvailable()) {
topLevel.get(className).put(id, flatMap);
// This is required to prevent a ConcurrentModificationException
List<String> parseObjectKeys = new ArrayList<>(parseObject.keySet());
for (final String key : parseObjectKeys) {
Object value = parseObject.get(key);
if (value instanceof String) {
flatMap.putString(key, (String) value);
} else if (value instanceof Boolean) {
flatMap.putBoolean(key, (Boolean) value);
} else if (value instanceof Integer) {
flatMap.putInt(key, (Integer) value);
} else if (value instanceof Double) {
flatMap.putDouble(key, (Double) value);
} else if (value instanceof Date) {
flatMap.putString(key, Utils.toISO8601UTC((Date)value));
}
else {
if (value != null &&
!(value instanceof ParseACL)) {
Log.e(TAG, "Unknown type: " + value.getClass());
}
}
}
}
}
}
HashMap is not a thread-safe data structure, so you can use ConcurrentHashMap instead of HashMap.
use ConcurrentHashMap<String, ConcurrentHashMap<String, WritableMap>> topLevel
instead of
HashMap<String, HashMap<String, WritableMap>> topLevel.
and put List<String> parseObjectKeys = new ArrayList<>(parseObject.keySet());
in synchronized block.
Hope it will help.
Solution!!!
Add synchronized block like this
private synchronized void convertParseObject() { }
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());
}
}
}
}