I'm developing an Android app which will initiate a number of API calls that return JSON data structures and then store the results in a content provider. Different API calls return different JSON data structures and map to a corresponding table schema in the content provider. I'm looking for a simple and Java-esque method to map from the properties in a JSONObject to a flat ContentValues object. I started to use a simple HashMap and iterating over it's entrySet mapping key strings in the JSONObject to value strings for the ContentValues object, but I'd like to account for the fact that some JSON properties are integers or booleans. Also, in some cases I'd like a more complex mapping such as a JSONArray into a comma separated string. In C, I'd probably just do this with a struct array name, value, type, and an optional callback to handle more complex mappings.
UPDATE: Due to the hierarchal nature of the JSON Data Structure and due to the fact that it can actually have sub-tables at certain depths I've taken the following approach.
private static interface MapJSON {
public void mapData(JSONObject object, ContentValues values)
throws JSONException;
}
private static abstract class AbstractMapJSON implements MapJSON {
protected final String mJSONName;
protected final String mContentName;
public AbstractMapJSON(String jsonName, String contentName) {
mJSONName = jsonName;
mContentName = contentName;
}
public abstract void mapData(JSONObject object, ContentValues values)
throws JSONException;
}
/* This is the basic template for each of the basic types */
private static class BooleanMapJSON extends AbstractMapJSON {
public BooleanMapJSON(String jsonName, String contentName) {
super(jsonName, contentName);
}
public void mapData(JSONObject object, ContentValues values)
throws JSONException {
values.put(mContentName, object.getBoolean(mJSONName));
}
}
/* This class takes a nested JSON Object and flattens it into the same table */
private static class ObjectMapJSON implements MapJSON {
protected final String mJSONName;
protected final MapJSON[] mMap;
public ObjectMapJSON(String jsonName, MapJSON[] map) {
mJSONName = jsonName;
mMap = map;
}
public void mapData(JSONObject object, ContentValues values)
throws JSONException {
JSONObject subObject = object.getJSONObject(mJSONName);
for(MapJSON mapItem: mMap) {
mapItem.mapData(subObject, values);
}
}
}
With that defined, I've can create mappings like this:
private static final MapJSON[] mainSiteMap = new MapJSON[] {
new StringMapJSON("name", StackPad.Sites.NAME),
new LongMapJSON("creation_date", StackPad.Sites.CREATION_DATE),
new StringMapJSON("description", StackPad.Sites.DESCRIPTION),
};
private static final MapJSON sitesMap = new ObjectMapJSON("main_site", mainSiteMap);
But it still seems like it needs a little work to mesh well.
Maybe you can build a class and use it in the hashmap , I dont know whats your types but for example
class Foo{
String name;
String value;
String type;
int opt;
}
.....
HashMap hm = new HashMap();
Foo foo = new Foo("123","123","type",1);
hm.put("100", foo);
.....
you can try using google's gson, create a structure for your object then map them to the object.. you can specify what datatype and support primitive types as well..
Related
i have the following method
public static <E> APIGatewayProxyResponseEvent generateResponse(E request, E response, int statusCode){
JSONObject result = new JSONObject();
result.put(Constants.REQUEST, request);
result.put(Constants.RESPONSE, response);
return new APIGatewayProxyResponseEvent()
.withBody(result.toString())
.withStatusCode(statusCode)
.withHeaders(Constants.commonHeaders);
}
i am getting net.sf.json.JSONException: java.lang.reflect.InvocationTargetException when result.put(Constants.RESPONSE, response); is executed
response is
Also the corresponding class is:
public class PhysicalMediaURL extends MediaURL {
private static final String IDENTIFIER_PREFIX = "images/I/";
public PhysicalMediaURL(String physicalId, String extension, MediaHostnameProvider mediaHostnameProvider) {
super("images/I/" + physicalId, extension, mediaHostnameProvider);
}
}
public abstract class MediaURL implements URL {
private final String identifier;
private final String extension;
private final MediaHostnameProvider mediaHostnameProvider;
public MediaURL(String identifier, String extension, MediaHostnameProvider mediaHostnameProvider) {
this.identifier = identifier;
this.extension = extension;
this.mediaHostnameProvider = mediaHostnameProvider;
}
public String getIdentifier() {
return this.identifier;
}
public String getExtension() {
return this.extension;
}
public String getDomainName() {
return this.mediaHostnameProvider.getMediaHostname(this.getExtension());
}
public String getURL() {
StringBuilder urlBuilder = new StringBuilder();
urlBuilder.append("https://");
urlBuilder.append(this.getDomainName());
urlBuilder.append('/');
urlBuilder.append(this.getIdentifier());
urlBuilder.append('.');
urlBuilder.append(this.getExtension());
return urlBuilder.toString();
}
public List<String> getStyleTags() {
return null;
}
}
where PhysicalMediaURL is of type: URL and that is an interface
public interface URL {
String getIdentifier();
String getDomainName();
String getExtension();
List<String> getStyleTags();
String getURL();
}
I am a bit stuck in this.. need help.
First off it looks like you are using a JSON implementation that is not updated as regularly as the other ones(Your exception is from net.sf.json). I always recommend using the org.json implementation as it receives regular updates and bugfixes.
Most implementations of JSONObject, when used in this form, use bean based reflection to retrieve values from your object. This is not always what you want when your object is in an inheritance hierarchy because, depending on the object and the JSONObject impl, it will pull fields from the implementation that are not on your higher level type(URL in this case).
If you really want a generic serialization function use something like Jackson or Gson that will allow you to specify the type as a part of the serialization. Otherwise consider transforming your objects, before they are passed to your generateResponse function, into simpler objects such as a Map<String, String> that can serialize unambiguously.
As a final thought JSONObject's generic serialization works, but, its performance is likely to be worse than using a dedicated higher level serializer like Jackson. It's best used with the explicit put methods to generate simple objects.
So, lets say I have an enum, "Data".
public enum Data {
FIRSTNAME(String.class, "John");
private final Class<?> defaultClass;
private final Object defaultData;
Data(Class<?> clazz, Object data) {
this.defaultClass = clazz;
this.defaultData = data;
}
public Class<?> getDataClass() {
return this.defaultClass;
}
}
Would it be possible to create a method that gets its return type based on the passed Data enum's getDataClass() response? Ie like this:
//This code obviously won't work, it's just another way of showing this.
public [data.getDataClass()] getData(Data data) {
//Return the data.
}
I am writing a Java library for interacting with metrics from Graphite.
A typical JSON response looks like this (taken from the official docs):
[{
"target": "entries",
"datapoints": [
[1.0, 1311836008],
[2.0, 1311836009],
[3.0, 1311836010],
[5.0, 1311836011],
[6.0, 1311836012]
]
}]
where the first element of the "datapoints" array is the value and the second one the timestamp. I have modelled a GraphiteDataset class as follows
class GraphiteDataset {
private String target;
private List<GraphiteDatapoint> datapoints;
....
}
and the GraphiteDatapoint class
class GraphiteDatapoint {
private Long timestamp;
private Double value;
...
}
Now I need to parse the response (see above) into the GraphiteDataset
class using Gson. Unfortunately, the elements of "datapoints" are not named objects (e.g. {timestamp: 1234, value: 1.0} but a 2 dimensional array so I cannot directly deserialize it into some class. Currently my solution is to have an intermediate class
class GraphiteIntermediateDataset {
private String target;
private List<String> datapoints;
...
}
which has the datapoints as Strings and then I parse them into the appropriate GraphiteDatapoint instance. I think that I cannot work around a custom deserializer. Do you have any suggestions or tricks how to make this a little more convenient?
The JSON [1.2, 123456] is a array of a Double and a Long, but they are both Number, so try this:
class GraphiteDataset {
private String target;
private List<List<Number>> datapoints;
....
}
Then convert datapoints into your type after parsing, with something like:
List<GraphiteDatapoint> points = datapoints.stream().
.map(nums -> new GraphiteDatapoint(nums.get(0).doubleValue(), nums.get(1).intValue()))
.collect(Collectors.toList());
assuming a constructor like:
class GraphiteDatapoint {
private Long timestamp;
private Double value;
public GraphiteDatapoint(Double value, Long timestamp) {
this.value = value;
this.timestamp = timestamp;
}
...
}
The final solution is to introduce an intermediate class GraphiteIntermediateDataset which looks as follows:
class GraphiteIntermediateDataset {
private String target;
private List<List<Number>> datapoints;
}
and the deserializer code looks like this
List<GraphiteIntermediateDataset> intermediateDatasetList = GSON.fromJson(raw, new TypeToken<List<GraphiteIntermediateDataset>>(){}.getType());
GraphiteIntermediateDataset intermediateDataset = intermediateDatasetList.get(0);
... check if empty (which can happen), when true return an empty GraphiteDataset
List<GraphiteDatapoint> gDatapoints = intermediateDataset
.stream()
.map(ds -> {
return new GraphiteDatapoint(ds.get(0).longValue(),
ds.get(1).doubleValue())
}
.collect(Collectors.toList());
return new GraphiteDataset()
.setDatapoints(gDatapoints);
Type safety and proper data binding are your friends. Gson has several methods to accomplish what you need. For example, declare data transfer objects:
final class GraphiteDataset {
final String target;
// The incoming DTO has property `datapoints`, however Java conventions suggest dataPoints (as far as I understand English).
#SerializedName("datapoints")
final List<GraphiteDataPoint> dataPoints;
// Actually, Gson does not need this constructor, and the DTO can even have a single private default one.
// But in order to make it consistent with the next class just making it programmatically instantiable...
// Also, but may be opinion-based, hiding constructors is really a good idea since one can hide the instantiation strategy whilst constructors cannot.
private GraphiteDataset(final String target, final List<GraphiteDataPoint> dataPoints) {
this.target = target;
this.dataPoints = dataPoints;
}
}
final class GraphiteDataPoint {
final double value;
final long timestamp;
private GraphiteDataPoint(final double value, final long timestamp) {
this.value = value;
this.timestamp = timestamp;
}
// Instantiation must be accessible programmatically somehow
static GraphiteDataPoint graphiteDataPoint(final double value, final long timestamp) {
return new GraphiteDataPoint(value, timestamp);
}
}
And then implement either a GraphiteDataPoint JSON deserializer:
// In Gson serializers and deserializers can only deal with intermediate Gson JSON tree representation of objects (JsonElement-s).
// For some cases it's quite simple, if the given data to serialize/deserialize does not consume much memory
final class GraphiteDataPointJsonDeserializer
implements JsonDeserializer<GraphiteDataPoint> {
private static final JsonDeserializer<GraphiteDataPoint> graphiteDataPointJsonDeserializer = new GraphiteDataPointJsonDeserializer();
private GraphiteDataPointJsonDeserializer() {
}
// Not letting to instantiate a stateless (so it's thread-safe) deserializer twice or more
static JsonDeserializer<GraphiteDataPoint> getGraphiteDataPointJsonDeserializer() {
return graphiteDataPointJsonDeserializer;
}
#Override
public GraphiteDataPoint deserialize(final JsonElement jsonElement, final Type type, final JsonDeserializationContext context)
throws JsonParseException {
final JsonArray asJsonArray = jsonElement.getAsJsonArray();
final double value = asJsonArray.get(0).getAsJsonPrimitive().getAsDouble();
final long timestamp = asJsonArray.get(1).getAsJsonPrimitive().getAsLong();
return graphiteDataPoint(value, timestamp);
}
}
Or a type adapter:
// Type adapters, unlike serializers and deserializers, are designed to work with streams.
// They may look too low-level and tedious/hard to implement, but for some cases they can be useful in both serialization and deserialization.
// For the case #1: no need to serialize nested objects recursively to transform them to JSON trees that can be important for large objects.
// For the case #2: intermediate JSON trees are not necessary (but internal buffers are).
final class GraphiteDataPointTypeAdapter
extends TypeAdapter<GraphiteDataPoint> {
private static final TypeAdapter<GraphiteDataPoint> graphiteDataPointTypeAdapter = new GraphiteDataPointTypeAdapter();
private GraphiteDataPointTypeAdapter() {
}
static TypeAdapter<GraphiteDataPoint> getGraphiteDataPointTypeAdapter() {
return graphiteDataPointTypeAdapter;
}
#Override
public void write(final JsonWriter out, final GraphiteDataPoint value) {
throw new UnsupportedOperationException("not implemented");
}
#Override
public GraphiteDataPoint read(final JsonReader in)
throws IOException {
in.beginArray();
final double value = in.nextDouble();
final long timestamp = in.nextLong();
in.endArray();
return graphiteDataPoint(value, timestamp);
}
}
Both implementations are essentially the same, but may be crucial for you dependening on data (de)serialization strategies and costs. Example use:
private static final String JSON = "[{\"target\":\"entries\",\"datapoints\":[[1.0,1311836008],[2.0,1311836009],[3.0,1311836010],[5.0,1311836011],[6.0,1311836012]]}]";
// Gson is thread-safe and can be shared between threads, so no need to instantiate it every time it's needed
private static final Gson gsonWithDeserializers = new GsonBuilder()
.registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointJsonDeserializer())
.create();
private static final Gson gsonWithTypeAdapters = new GsonBuilder()
.registerTypeAdapter(GraphiteDataPoint.class, getGraphiteDataPointTypeAdapter())
.create();
private static final TypeToken<List<GraphiteDataset>> graphiteDatasetsTypeToken = new TypeToken<List<GraphiteDataset>>() {
};
public static void main(final String... args) {
dumpGraphiteDatasets(gsonWithDeserializers.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
dumpGraphiteDatasets(gsonWithTypeAdapters.fromJson(JSON, graphiteDatasetsTypeToken.getType()));
}
private static void dumpGraphiteDatasets(final Iterable<GraphiteDataset> graphiteDatasets) {
graphiteDatasets.forEach(graphiteDataset -> {
out.println(graphiteDataset.target);
graphiteDataset.dataPoints.forEach(graphiteDataPoint -> {
out.print(" ");
out.print(graphiteDataPoint.value);
out.print(" ");
out.println(graphiteDataPoint.timestamp);
});
});
}
The output:
entries
1.0 1311836008
2.0 1311836009
3.0 1311836010
5.0 1311836011
6.0 1311836012
entries
1.0 1311836008
2.0 1311836009
3.0 1311836010
5.0 1311836011
6.0 1311836012
I have a data structure in Java that I am populating via different methods. One method populates it from an API, another method populates it from parsing some HTML, another populates it a different way, etc. Basically, a method for every data source that could populate it. What I'm wondering is, what design patterns are available in Java for this? What's the best/cleanest OOP approach to this problem?
E.g.,
public class Data {
private String foo;
private List<String> bar;
private Map<String, Integer> baz;
public Data (String foo, List<String> bar, Map<String, Integer baz) {
this.foo = foo;
this.bar = bar;
this.baz = baz;
}
// Setters and Getters here, etc
}
public class FacebookParser {
private Document dom;
public static Data parse(Document dom) {
// Parse document
// Create Data object
return Data;
}
}
public class TwitterParser {
private Document dom;
public static Data parse(Document dom) {
// Parse Twitter
Data d = new Data(stuff from twitter);
return d;
}
}
You want a Data and it is represented in different forms. The part that you are interested in should be defined in an abstract way. So making the Data an interface is a good point for starting.
public interface Data {
String getFoo();
List<String> getBar();
Map<String, Integer> getBaz();
}
This data is obtained from different providers. The common thing is we need someone to provide Data. In the end, the only thing we are interested in is the Data itself, not how it is parsed or provided. So we need a simple DataProvider interface.
public interface DataProvider {
Data createData();
}
Now we can implement the provider classes those know how to fetch, parse, process etc. the data. Provider classes should not be dealing with how to convert the provider specific data into our common Data interface. They are only responsible for creating a Data implementation that they know.
public class FacebookDataProvider implements DataProvider {
public Data createData() {
FacebookSpecificInfo x = ...
FacebookData data = new FacebookData();
// Note that this class does not know anything about foo, bar and baz.
// We are still Facebook context.
data.setName(x.getName());
data.setValues(x.getValues());
data.setHeaders(x.getHeaders());
return data;
}
}
class FacebookData implements Data {
private String name;
private List<String> values;
private Map<String, Integer> headers;
void setName(String name) { this.name = name; }
void setValues(String values) { this.values = values; }
void setHeaders(String headers) { this.headers = headers; }
// This is the part where we switch the context and convert
// Facebook specific data into our expected Data
// ie. Facebook's name field corresponds my foo field.
public String getFoo() { return name; }
public List<String> getBar() { return values; }
public Map<String, Integer> getBaz() { return headers; }
}
What you can do is have a separate class for setting the values of the Data class.
You can have something like this :
public class DataPopulator{
public void setTwitterData(Data d){
//your data
}
public void setFacebookData(Data d){
//your data
}
}
This is something similar to Adapter Design pattern, though not exactly same.
You can have a look at it here.
I am trying to reconstruct a bean using BeanUtils. The BeanUtils.populate(obj,map) is almost exactly what I need except I think I need a converter as one of the things in the map is an arraylist but the bean needs an array[]. I have tried to write a converter for it but it doesn't go into the convert method. Calling populate gives IllegalArgumentException with message 'argument type mismatch'
Bean code:
public class TestObject implements Serializable
{
private double[] data;
public double[] getData() {
return data;
}
public void setData(double[] data) {
this.data = data;
}
}
Application Code:
public static void main(String[] args)
{
ConvertUtils.register(new DoubleArrayConverter(), double[].class);
Map<String, Object> result = getMap();
Class<?> clazz = Class.forName("com.test.TestObject");
Object obj = clazz.newInstance();
BeanUtils.populate(obj, result);
}
Converter code:
public class DoubleArrayConverter implements Converter
{
//takes in an arraylist and returns a double[]
public Object convert(Class arg0, Object arg1)
{
ArrayList list = (ArrayList)arg1;
double[] data = Doubles.toArray(list);
return data;
}
}
After adding the Google library that includes Doubles
import com.google.common.primitives.Doubles;
and setting some values into the map
Map<String, Object> result = new HashMap<String, Object>();
List<Double> listDouble = new ArrayList<Double>();
listDouble.add(123.45);
listDouble.add(678.90);
result.put("data", listDouble);
I was able to run your code and populate the bean. It is important that the map key 'data' agrees with the setData and getData methods.
Are you sure your Map has an array list in it?