io.keen.client.android.AndroidJsonHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of keen-client-api-android Show documentation
Show all versions of keen-client-api-android Show documentation
Android Client API for Keen IO
package io.keen.client.android;
import android.os.Build;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import io.keen.client.java.KeenJsonHandler;
/**
* Implementation of the {@link io.keen.client.java.KeenJsonHandler} interface using the built-in
* Android JSON library ({@link org.json.JSONObject}).
*
* @author Kevin Litwack ([email protected])
* @since 2.0.0
*/
public class AndroidJsonHandler implements KeenJsonHandler {
///// KeenJsonHandler METHODS /////
/**
* {@inheritDoc}
*/
@Override
public Map readJson(Reader reader) throws IOException {
if (reader == null) {
throw new IllegalArgumentException("Reader must not be null");
}
String json = readerToString(reader);
try {
JSONObject jsonObject = new JSONObject(json);
return JsonHelper.toMap(jsonObject);
} catch (JSONException e) {
throw new IOException(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public void writeJson(Writer writer, Map value) throws IOException {
if (writer == null) {
throw new IllegalArgumentException("Writer must not be null");
}
JSONObject jsonObject = convertMapToJSONObject(value);
writer.write(getJsonObjectManager().stringify(jsonObject));
writer.close();
}
/**
* Sets whether or not this handler should wrap nested maps and collections explicitly. If set
* to false, maps will be passed directly to JSONObject's constructor without any modification.
*
* Wrapping is necessary on older versions of Android due to a bug in the org.json
* implementation. For details, see:
*
* https://code.google.com/p/android/issues/detail?id=55114
*
* In general, clients of the SDK should never change the default for this value unless they
* have done extensive testing and understand the risks.
*
* @param value {@code true} to enable wrapping, {@code false} to disable it.
*/
public void setWrapNestedMapsAndCollections(boolean value) {
this.isWrapNestedMapsAndCollections = value;
}
///// PROTECTED METHODS /////
/**
* Sets the {@link JsonObjectManager} instance to use. By default this class will simply use
* the normal Android JSONObject methods, but this method may be used to provide a different
* implementation, such as a stubbed/mocked implementation for unit testing.
*
* @param jsonObjectManager The {@link JsonObjectManager} instance to use.
*/
protected void setJsonObjectManager(JsonObjectManager jsonObjectManager) {
this.jsonObjectManager = jsonObjectManager;
}
///// PROTECTED INNER CLASSES /////
/**
* Interface wrapping usage of JSONObjects.
*/
protected interface JsonObjectManager {
String stringify(JSONObject object);
JSONObject newObject(Map map);
JSONArray newArray(Collection> collection);
}
/**
* Default implementation of JsonObjectManager which just uses the JSONObject methods directly.
*/
private static class AndroidJsonObjectManager implements JsonObjectManager {
@Override
public String stringify(JSONObject object) {
return object.toString();
}
@Override
public JSONObject newObject(Map map) {
return new JSONObject(map);
}
@Override
public JSONArray newArray(Collection> collection) {
return new JSONArray(collection);
}
}
///// PRIVATE CONSTANTS /////
/**
* The size of the buffer to use when copying a reader to a string.
*/
private static final int COPY_BUFFER_SIZE = 4 * 1024;
///// PRIVATE FIELDS /////
/**
* Boolean indicating whether or not to wrap maps/collections before passing to JSONObject.
*/
private boolean isWrapNestedMapsAndCollections = (Build.VERSION.SDK_INT < 19);
/**
* Manager for creating JSONObjects and converting them to Strings; used for unit tests.
*/
private JsonObjectManager jsonObjectManager = null;
///// PRIVATE METHODS /////
/**
* Get the default jsonObjectManager, or use one that was explicitly specified.
*
* @return A default implementation which uses the normal Android library methods, unless a
* different implementation has been set explicitly.
*/
private JsonObjectManager getJsonObjectManager() {
if (jsonObjectManager == null) {
jsonObjectManager = new AndroidJsonObjectManager();
}
return jsonObjectManager;
}
/**
* Converts an input map to a JSONObject, wrapping any values in the map if wrapping is enabled
* and the map contains wrappable values (i.e. nested maps or collections).
*
* @param map The map to convert.
* @return A JSONObject representing the map.
* @throws IOException if there is an error creating the JSONObject.
*/
@SuppressWarnings("unchecked")
private JSONObject convertMapToJSONObject(Map map) throws IOException {
Map newMap;
// Only perform wrapping if it's enabled and the input map requires it.
if (isWrapNestedMapsAndCollections && requiresWrap(map)) {
newMap = new HashMap();
// Iterate through the elements in the input map.
for (Object key : map.keySet()) {
Object value = map.get(key);
Object newValue = value;
// Perform recursive conversions on maps and collections.
if (value instanceof Map) {
newValue = convertMapToJSONObject((Map) value);
} else if (value instanceof Collection) {
newValue = convertCollectionToJSONArray((Collection) value);
} else if (value instanceof Object[]) {
newValue = convertCollectionToJSONArray(Arrays.asList((Object[]) value));
}
// Add the value to the new map.
newMap.put(key, newValue);
}
} else {
// Use the input map as-is.
newMap = map;
}
// Pass the new map to the JSONObject constructor.
return getJsonObjectManager().newObject(newMap);
}
/**
* Converts an input collection to a JSONArray, wrapping any values in the collection if
* wrapping is enabled and the collection contains wrappable values (i.e. maps or nested
* collections).
*
* @param collection The collection to convert.
* @return A JSONArray representing the collection.
* @throws IOException if there is an error creating the JSONArray.
*/
@SuppressWarnings("unchecked")
private JSONArray convertCollectionToJSONArray(Collection collection) throws IOException {
Collection newCollection;
// Only perform wrapping if it's enabled and the input collection requires it.
if (isWrapNestedMapsAndCollections && requiresWrap(collection)) {
newCollection = new ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy