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.
Related
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class ItemSubstitutionRequestDTO {
public ItemSubstitutionRequestDTO()
{
}
private List<Map<String,Integer>> substituteFor=new ArrayList<Map<String,Integer>>();
private String orderId;
public List<Map<String,Integer>> getSubstituteFor()
{
return substituteFor;
}
public void setSubstituteFor(List<Map<String,Integer>> substituteFor)
{
this.substituteFor = substituteFor;
}
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
}
Final result ERROR:
java.util.Map is an interface, and JAXB can't handle interfaces.
I can't get JaxB to be able to marshall/unmarshall instances of Map.I tried other annotation also and found this is one of the possible way to solve the above error but nothing is woking.
Below is the input json which is coming from UI side
{ "itemSubstitutionRequestDTO": { "substituteFor": [{"41712":2}],
"orderId": "1073901", } }
You didn't write how your XML content within the
<substituteFor> element would look like.
Therefore I assume something like this:
<itemSubstitutionRequestDTO>
<substituteFor>
<item>
<key>x</key>
<value>23</value>
</item>
<item>
<key>y</key>
<value>3</value>
</item>
</substituteFor>
<orderId>abc</orderId>
</itemSubstitutionRequestDTO>
As the JAXB error message already told you,
it can't handle types with an interface between the < >,
like for example your List<Map<String,Integer>>.
However it can handle types with a normal class between < >,
like List<SubstitutionMap>.
So the first step is to rewrite your ItemSubstitutionRequestDTO class
so that it does not use List<Map<String,Integer>>, but instead List<SubstitutionMap>.
You need to write the SubstitutionMap class (not an interface) by yourself.
But it can be extremely simple:
public class SubstitutionMap extends HashMap<String, Integer> {
}
Now JAXB doesn't throw an error anymore, but it still doesn't know how to marshal/unmarshal a SubstitutionMap.
Therefore you need to write an XmlAdapter for it.
Let's call it SubstitutionMapAdapter.
To make JAXB aware of this adapter, you need to annotate the substituteFor
property in your ItemSubstitutionRequestDTO class with:
#XmlJavaTypeAdapter(SubstitutionMapAdapter.class)
The adapter's job is to do the actual conversion from SubstitutionMap
to an array of SubstitutionMapElements and vice versa.
Then JAXB can handle the SubstitutionMapElement array by itself.
public class SubstitutionMapAdapter extends XmlAdapter<SubstitutionMapElement[], SubstitutionMap> {
#Override
public SubstitutionMap unmarshal(SubstitutionMapElement[] elements) {
if (elements == null)
return null;
SubstitutionMap map = new SubstitutionMap();
for (SubstitutionMapElement element : elements)
map.put(element.getKey(), element.getValue());
return map;
}
#Override
public SubstitutionMapElement[] marshal(SubstitutionMap map) {
// ... (left to you as exercise)
}
}
The class SubstitutionMapElement is just a simple container for a key and a value.
#XmlAccessorType(XmlAccessType.FIELD)
public class SubstitutionMapElement {
private String key;
private int value;
// ... constructors, getters, setters omitted here for brevity
}
I have the following data model with custom attributes:
class Foo {
private Long id;
private Set<AdditionalAttribute> attributes;
}
class AdditionalAttribute {
private Key key;
private String value;
}
class Key {
private String name;
private Class<?> type;
}
My model produces this json:
{"id":123, "attributes": [{"key1":12345}, {"key2":"value2"}]}
My expected json is:
{"id":123, "key1":12345, "key2":"value2"}
How can I achieve a such serialization / deserialization using graphql spqr?
FYI, currently I can do it in REST API with jackson (BeanSerializerModifier for serialization and #JsonAnySetter for deserialization) as follow:
// Serialization using BeanSerializerModifier
class FooModifier extends BeanSerializerModifier {
#Override
public List<BeanPropertyWriter> changeProperties(
SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (int i = 0; i < beanProperties.size(); i++) {
BeanPropertyWriter writer = beanProperties.get(i);
if (Foo.class.isAssignableFrom(beanDesc.getBeanClass()) && "attributes".equals(writer.getName())) {
beanProperties.set(i, new FooAttributesWriter(writer));
}
}
return beanProperties;
}
}
class FooAttributesWriter extends BeanPropertyWriter {
public HasAttributesWriter(BeanPropertyWriter w) {
super(w);
}
#Override
public void serializeAsField(Object bean, JsonGenerator gen,
SerializerProvider prov) throws Exception {
if(Foo.class.isAssignableFrom(bean.getClass())) {
Set<AdditionalAttribute> set = ((Foo) bean).getAttributes();
for (AdditionalAttribute a : set) {
gen.writeStringField(a.getKey().getName(), a.getValue());
}
}
}
}
// Deserilization using #JsonAnySetter
class Foo {
private Long id;
private Set<AdditionalAttribute> attributes;
// Deserialization of custom properties
#JsonAnySetter
public void set(String name, Object value) {
attributes.add(new AdditionalAttribute(buildKey(name,value), value));
}
}
The problem here is not JSON (de)serialization. With GraphQL, the shape of all your inputs and outputs is defined by the schema, and the schema can not normally have dynamic parts (object types where the fields are unknown ahead of time). Because your Set<AdditionalAttribute> can contain anything at all at runtime, it means your Foo type would have to have unknown fields. This is highly antithetical to how GraphQL is designed.
The only way to achieve a dynamic structure is to have an object scalar which effectively is a JSON blob that can not be validated, or sub-selected from. You could turn Foo into such a scalar by adding #GraphQLScalar to it. Then all input would be valid, {"id":123, "key1":12345 "key2":"value2"} but also {"whatever": "something"}. And it would be your logic's job to ensure correctness. Additionally, if you ever return Foo, the client would not be able to sub-select from it. E.g. {foo} would be possible but {foo { id }} would not, because the schema would no longer know if the id field is present.
To recap, you options are:
Leaving it as it is (the dynamic stuff is a list nested under attributes)
Turning Set<AdditionalAttribute> into a type (a new class or EnumMap) with known structure with all the possible keys as fields. This is only possible if the keys aren't totally dynamic
Making the whole enclosing object an object scalar by using #GraphQLScalar
Thanks a lot for your time and the proposed options.
Currently, we have found another way (maybe option 4 :) ) to generate a "similar" json to the expected output (We lost the type information in the generated output, but we have another logic that helps us to retrieve the type).
Here an example :
class Foo {
private Long id;
private Set<AdditionalAttribute> attributes;
#GraphQLQuery
public String get(#GraphQLArgument(name = "key") String key) {
for (AdditionalAttribute a : attributes) {
if (a.getConfigurationKey().getKey().equalsIgnoreCase(key)) {
return a.getAttributeValue();
}
}
return null;
}
and we can sub-select Foo as follow:
foo {
id
key1: get(key: "key1")
key2: get(key: "key2")
}
And this return
{"id":123, "key1":"12345", "key2":"value2"}
I have multiple types of objects, I'd like to generalise the 'id' of the objects in a way that will dynamically change what field is selected as the id.
Example
public class ObjectA{
//Attribute name attA
private String attA;
.... More attributes
public String getAttA(){
return attA
}
.....More getters/setters
}
public class ObjectB{
//Attribute named attB
private String attB;
.... More attributes
public String getAttB(){
return attB
}
.... More getters and setters
}
Id like to be able to run something like this:
Map<????, ????> customIdMap = new HashMap<>();
//We decide that ObjectA main attribute is AttA
customIdMap.add(ObjectA.class, ObjectA::getAttA);
//We decide that ObjectB main attribute is AttB
customIdMap.add(ObjectB.class, ObjectB::getAttB);
Then I'll be able to have a list of general objects and ill be able to retrieve their ids from the map if it is a known object with:
public String getCustomId(Object object){
if(customIdMap.contains(object.getClass()){
//Parameters are messed up, but this is the general idea of how
//i thought this would look
return customIdMap.get(object.getClass()).apply(object);
}
}
The code above does not run since getAttA is a call to a none static method in a static context so i assume this maybe should be wrapped in some kind of generic object.
Can it be done?
Preferably you change ObjectA and ObjectB to have a common interface. If that's not possible you can put them into a map like this:
Map<Class<? extends Object>, Function<Object, String>> map = new HashMap<>();
map.put(ObjectA.class, a -> ((ObjectA) a).getAttA());
map.put(ObjectB.class, b -> ((ObjectB) b).getAttB());
EDIT:
Or if you would like to encapsulate it into a typesafe heterogeneous container:
public static class ToIdMap {
private final Map<Class<?>, Function<Object, String>> map = new HashMap<>();
public <X> void put(Class<X> clazz, Function<X, String> func) {
map.put(clazz, (Function<Object, String>) func);
}
public String toIdString(Object o) {
return map.get(o.getClass()).apply(o);
}
}
EDIT2: Note that neither of these solutions work for subclasses, but it could be supported by traversing the class hierarchy in toIdString.
Your wording is a bit unclear, but I assume you want to get the ID of an object, even when they are different classes. This is the problem that interfaces solve.
You can create an interface, with one method called getId(), which will return the id. Then, you can just call getId() on any type of object with an id.
For example:
public interface Identifiable {
String getId();
}
public class ObjectA implements Identifiable {
// same for ObjectB
#Override
public String getId() {
return id;
}
}
Then, in your code:
Identifiable i1 = new ObjectA();
Identifiable i2 = new ObjectB();
System.out.println(i1.getId());
System.out.println(i2.getId());
EDIT:
It still looks like an interface is the cleanest way of solving your problem. For completeness, the following will work:
Map<Class, Function<?, String> map = new HashMap<>();
map.put(Object1.class, (Object1 o) -> o.getAttrA); // repeat for ObjectB
It can then be called with:
if (obj instanceof Object1) return map.get(Object1.class).apply((ObjectA) obj);
Ended up doing this weird solution:
class Mapping<T> {
private Function<T, String> idFunc;
public Mapping(Function<T, String> idFunc) {
this.idFunc = idFunc;
}
public String apply(T obj) {
return idFunc.apply(obj);
}
}
}
private Map<Class, Mapping> mappings = new HashMap<>();
mappings.put(ObjectA.class, new Mapping<>(ObjectA::getAttA);
mappings.put(ObjectB.class, new Mapping<>(ObjectB::getAttB);
public String getObjectID(Object object){
String id = null;
if(mappings.containsKey(object.getClass())){
id = mappings.get(object.getClass()).apply(object);
}
return id;
}
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'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..