Who should take care of filling data class? Parser or Class? - java

I'm having some doubts about software design when parsing an XML. Usually, I will create a class for every meaningful tag in the XML and then from the parser fill it with the attributes.
setElementListener(new ElementListener() {
#Override
public void start(Attributes attributes) {
Object obj = new Object();
obj.setAttribute(attributes.get("attName");
}
}
Now, I have a big XML with many differentes objects, so my XMLParser class is becoming unstoppable from growing. We discussed that maybe in our object class create a static method that will handle the creation of the object from the attributes.
public static Object inflate(Attributes attributes) {
Object result = new Object();
obj.setAttribute(attributes.get("attName");
return result;
}
Which of both ways is better?
EDIT:
The main problem with the second option is that I don't like the data class to know what Attributes is, it seems like not a good design.

Related

Writing child instance variables to JSONObject

So recently I figured out how to work with JSON in java, and created code that writes, reads and updates information (mostly classes) to and from my JSON database. In my class 'Activity' I have a method that writes the class object to JSON:
public void createActivity() {
File file = database;
JSONObject newActivity = new JSONObject();
setRegDate(LocalDate.now());
try {
actID = IO.getJsonArray(file, "Activities").length() + 1;
} catch (Exception e) {
System.out.println("Exception: Could not set new activity ID.");
}
newActivity.put("userID", userID);
newActivity.put("actDate", actDate);
newActivity.put("regDate", regDate);
newActivity.put("actID", actID);
newActivity.put("description", description);
newActivity.put("coach", coach);
try {//Writes new user JSONObject to account file.
IO.putObjInArr(file, newActivity, "Activities");
} catch (Exception e) {
System.out.println("Exception: Creating activity failed.");
}
}
As the learning process continue, I have now added child-classes to my project. One child-class may contain the instance variables 'distance' and 'time'. There are several child-classes.
Now of course, I do not want to copy the above method to every child-class and there add the specific variables to it. I want all of this centralised in one parent-class.
I wonder, is it possible to somehow loop over all of the possible child-classes' variables, so that I can write those to JSON? Or are child-variables simply not visible to the parent, let alone if I don't specifiy to the parent which variables those might be?
For now, all that I can think of is to put all instance variables of the child class in a hashmap, send them as arguments to Activity.createActivity, and there loop over all the elements of the hashmap.
The problem you've faced has two main causes:
The functionality of this method is tightly coupled to the needs of a particular class. And as a consequence can be difficult to reuse in other classes.
The method itself violates the first principle of SOLID, the Single responsibility principle, which states that a class should have only one reason to change. But there's a whole lot of things happening in createActivity(): it reads JSON from a file, it alters the JSONobject, it updates the file.
Basically, all pieces of functionality that can be observed createActivity() should not be coupled to any class. Instead, this method can be split into several static methods, each representing a separate responsibility. And this method can be grouped into a Utility class.
That's how such class might look like:
public class JsonUtils {
private JsonUtils() {} // no way and no need to invoke the constructor of this class
public JSONObject readFromFile(Path path) throws IOException {
String json = Files.readString(path);
return new JSONObject(json);
}
public void updateJsonObject(Map<String, ?> map, JSONObject json) {
map.forEach(json::put);
}
public void writeToFile(Path path, JSONObject json) throws IOException {
Files.writeString(path,json.toString());
}
public void writeToFile(Path path, JSONObject json, OpenOption... options) throws IOException {
Files.writeString(path,json.toString(), options);
}
// other methods
}
Note: File class is legacy. The recommended alternative is to use Path from NIO API. And if you make use of Files utility class from NIO.2 as shown in the example above, it would immensely simplify code of IO-processing methods.

Gson, Deserialize Wrapper class

I need help with the gson library, basically i have to de-serialize a wrapper class defined like this:
static class WrapperClass {
public int value;
private final String otherData;
public WrapperClass(String otherData) {
this.otherData = otherData;
}
}
The class that I have to "jsonize" has fields like this:
private final WrapperClass wrappedData = new WrapperClass("other data");
The serializer of the wrapper class is really simple:
#Override
public JsonElement serialize(WrapperClass src, Type typeOfSrc, JsonSerializationContext context) {
return context.serialize(src.value);
}
The problem is the de-serialization side, I should not re-instantiate the WrapperClass object because it's already there by default, i have only to de-serialize it's value, it's possible to this with the gson library?
So, what you want to do: "read" the content of some JSON string and "overlay" existing objects with that.
I am not aware of a built-in way to do that. Beyond that: doing something of this complexity under the covers sounds like the wrong approach, too.
Thus, my recommendation: clearly separate those responsibilities. Meaning: instead of of creating a (almost counter-intuitive) solution that merges JSON data "into" existing objects: do that explicitly, it step by step. Like:
read the JSON data and de-serialize into one or more objects
then have an another special component "update" your "old" objects with the information found in the de-serialized objects

xstream marshal to XML - add attribute based on number of child nodes

I'm pretty new to xstream.
I'm working on a model class that looks like the following:
#XStreamAlias("MyRootClass")
public class MyRootClass {
// A bunch of other classes as child nodes
#XStreamAlias("MyClassList")
private List<MyClass> foo;
}
Now, is there a way for me to produce the following XML when marshalling, without modifying the class?
<MyRootClass>
<!-- a bunch of other class nodes -->
<MyClassList COUNT="3">
<MyClass>MyClass 1</MyClass>
<MyClass>MyClass 2</MyClass>
<MyClass>MyClass 3</MyClass>
</MyClassList>
</MyRootClass>
The main issue is how to add the attribute "COUNT" to the list of MyClass gracefully. It will always show the number of MyClass inside MyClassList.
I am not allowed to modify the model class. However, I can implement my own converter to achieve the above.
The question is: what's the best way to do it?
There are lots of other (complex) classes within MyRootClass, and they've all been aliased & annotated. So, creating a 'marshal' method from scratch might be overkill?
welp, I figured it out.
Not sure if this is the "best" way to do it, but it works.
the key is using converters and nested converters.
this is what I ended up doing:
public class MyRootClassConverter implements Converter {
#Override
public boolean canConvert(Class type) {
return type.equals(MyRootClassConverter.class);
}
#Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
MyRootClass rootClass= (MyRootClass) source;
// as necessary
writer.startNode("Other Class1");
writer.setValue("Other Class Value");
writer.endNode();
if (rootClass.getMyClassList() != null || !rootClass.getMyClassList().isEmpty()) {
writer.startNode("MyClassList");
writer.addAttribute("COUNT", String.valueOf(rootClass.getMyClassList().size()));
for (MyClass child : rootClass.getMyClassList()) {
writer.startNode("MyClass");
context.convertAnother(child); // this is where the nesting happens
writer.endNode();
}
writer.endNode();
}
}
}

Building complex object inside or outside the class in Java?

I am trying to get the best practice in Java to solve the following problem :
I want to setup an complex object in one place, so that other clients can then reuse this construction. Here is an example and how I proceed :
My complex object is of type "Ontology", this object contains many parameters, once it is instantiated and filled, it is used in many objects as a kind of configuration by using its getter.
My Ontology.class
abstract class Ontology {
List<Something1> param1;
List<Something2> param2;
...
protected void addParam1(){
...
}
....
abstract protected void setup();
}
A way to hold the complex construction :
public class SpecificOntology extend Ontology{
#Override
protected void setup(){
addParam1(new Something(...));
...
}
}
A client :
protected void something(){
SpecificOntology o = new SpecificOntology();
o.setup();
install(o.getParam1(());
...
}
Another solution could be to make Ontology not abstract, make its Adder public and build the object outside of the class, but I don't know which pattern could be used for that ? I know the builder pattern and the factory pattern but I am not sure this is the right place for that. Any idea ?
If you want to build an object with many parameters, the first thing I would think of is the Builder pattern.

General Java class design for property with Collection which needs to listen for change

All,
Hopefully a simple question. I am thinking of the best way to implement a class which holds a number of collections and HashMaps where the class needs to know about when they have been modified outside of the class - i.e. added/removed/changed items. Each collection/hashmap needs to be exposed as a public getter in my class at the moment.
So my basic class looks like as follows...
public class MyClass {
protected final HashMap<String, String> _values = new HashMap<String, String>();
protected final ArrayList<MyOtherClass> _other = new ArrayList<MyOtherClass>();
protected final ArrayList<MyOtherClass2> _other2 = new ArrayList<MyOtherClass2>();
// ... implementation
public HashMap<String, String> getValues() {
return _values;
}
public ArrayList<MyOtherClass> getMyOtherClassList() {
return _other;
}
public ArrayList<MyOtherClass2> getMyOtherClassList2() {
return _other2;
}
public String getContent() {
// build the content based on other/other2...
StringBuilder sb = new StringBuilder();
// iterate through both collections to build content...
// ...
return sb.toString();
}
}
public getMyOtherClass {
public String name; // has getter and setter
public String value; // has getter and setter
}
public getMyOtherClass2 {
public String name; // has getter and setter
public String value; // has getter and setter
public String somethingElse; // has getter and setter
}
I want to add a key/value to the _values based on the length of the content i.e.-
_values.add("Length", getContent().length);
So the Length value is dynamic based on what gets added to the _other and _other2.
The problem with this is exposing the _values and _other with public getters is that anything outside the class can modify them. The class will not know if items have been modified.
A couple of solutions I can think of is to make the collection/hashmap readonly - but this throws a runtime exception - if this was the case I'd like the compiler to indicate that they are read-only and throw an exception but I don't know if this is possible.
The other way would be to add a add/remove for each of the collections/maps and update the Length property accordingly - but again, if the values change in the MyOtherClass, MyClass will still not know about it.
Alternatively write my own Hashmap/List/Collection to determine when items are added/removed, and possibly have a property change listener on the getMyOtherClass, getMyOtherClass2.
Any nice solutions to this?
Thanks,
Andez
Overide the map/list implementations and insert a call-back into the add/update/remove methods that triggers an update function on the parent.
Also it's bad form to create references directly to the implementations - this is better (read up on polymorphism for reasoning):
private Map<String,String> myMap = new HashMap<String,String>();
private List<String> myList = new List<String>();
In this case you can make use of some fundamentals of the Observer design pattern to have an Object "watching" the Maps and registering each change is made to them.
Create an object contains a Map and another object that contains a List, so since you have 1 map and 2 lists you'll have 3 of those "Observable" objects. Let's name the classes "ObservableMap" and "ObservableList". You can even create an abstract class "ObservableObject" and extend it with the previously mentioned classes.
These objects won't override the Map/List implementation, they'll only act as a wrapper by wrapping the methods you'll want to track to register the state and derive the call to modify the collection. For example, I'll post some code of the ObservableMap class (I'm instantiating the map with <String,String> but you can use generics here too if it suits you).
public Class ObservableMap extends ObservableObject{
private Map<String,String> map = new LinkedHashMap<String,String>();
private Watcher observer = new Watcher();
//Example of one of the wrapper methods (the other ones are like this one)
public void putObject(String key, String value) {
watcher.notifyPut(); //You can name the method the way you like and even pass
//the new key/value pair to identify what has been added.
map.put(key,value);
}
}
Here, the Watcher class is the one that registers the canges. It can either be a completely new Object (like in this case) or you can make an Interface and implement it in an existing class of yours to be able to set it as a watcher on your Observable objects.
Not 100% sure what you are asking. If you are trying to track changes to attributes to two classes you may wish, as other people have mentioned implement the observer pattern and raise notifications in the set methods. An alternative, that I have used successfully for implementing an undo mechanism is to use aspectJ or some other AOP (Aspect Orientated Programming) tool to intercept the set methods and perform the required notifications/updates that way.
Alternatively define an interface that only provides access to the getXXX operations and return those from your model, that way nothing can change the data.

Categories

Resources