Search code examples
javajsonparent-childorg.json

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.


Solution

  • 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.