3

I want to modify a java object by passing a json String to my application. The String will not contain all information about the complete, modified object but merely a single member that's meant to be set.

class SomeClass {

    Object var1 = "Hello";
    Object var2 = "AAA";

    // A lot of fields goes here ...

}

public AppTest() throws Exception {

    SomeClass myObject = new SomeClass();
    myObject.var2 = "BBB";

    String modification = "{\"var1\":\"Goodbye\"}";

    Gson gson = new Gson();
    SomeClass modifed = gson.fromJson(modification, SomeClass.class);

    // TODO: Merge a modifed object into myObject somehow

}

Furthermore, some of the fields might be objects with any number of fields. Again, I might want to just modify a single primitive inside the child object. A more complex example:

class SomeOtherClass {

    String var4 = "444";
    String var5 = "555";

}

class SomeClass {

    Object var1 = "111";
    Object var2 = "222";
    SomeOtherClass var3 = new SomeOtherClass();

}

public AppTest() throws Exception {

    SomeClass myObject = new SomeClass();
    myObject.var2 = "AAA";
    myObject.var3.var5 = "BBB";

    String modification = "{\"var3\":{\"var5\":\"XXX\"}}";

    Gson gson = new Gson();
    SomeClass modifed = gson.fromJson(modification, SomeClass.class);

    // TODO: Merge the modifed object into myObject somehow

}

So, my question is how can I partially modify an object with JSON?

Cœur
  • 37,241
  • 25
  • 195
  • 267
birgersp
  • 3,909
  • 8
  • 39
  • 79

3 Answers3

1

Hi I tried out one pseudo sample as below:

static Object merge(Object target, Object modified) {

        for (Field f : target.getClass().getDeclaredFields()) {
            if (!f.isAccessible()) {
                f.setAccessible(true);
            }
            if (f.getType().isPrimitive()) {

                    try {
                        if (f.getType().isAssignableFrom(java.lang.Boolean.TYPE)
                                && f.getBoolean(modified) != f.getBoolean(target)) {
                            f.setBoolean(target, f.getBoolean(modified));
                        } else if (f.getType().isAssignableFrom(java.lang.Character.TYPE)
                                && f.getChar(modified) != f.getChar(target)) {
                            f.setChar(target, f.getChar(modified));
                        } else if (f.getType().isAssignableFrom(java.lang.Integer.TYPE)
                                && f.getInt(modified) != f.getInt(target)) {
                            f.setInt(target, f.getInt(modified));
                        }
                        //....
                        // do it for all other primitive types
                       //also consider Enum types(not primitive so check 'f.getType().isEnum()') 
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }


            } else if (f.getType().getPackage().getName().matches("java.lang.*")
                    || f.getType().getPackage().getName().matches("java.util.*")) {
                /* Here I am trying to directly assign changes for the basic packages, if you want more you can add more packages*/

                try {
                    if (f.get(modified) != null && f.get(target) != f.get(modified)) {
                        f.set(target, f.get(modified));
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } else {
                /* If local classes encountered as member variables then do the same merge!*/
                try {
                    merge(f.get(target), f.get(modified));
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
        return target;

    }

you can call this method as myObject = (SomeClass) merge(myObject, modifed);

Note: It is not a fully functional method to do your job, you please read the comments inline and make it as a perfect for your case. I only ensure basic functionality

Tom Sebastian
  • 3,373
  • 5
  • 29
  • 54
  • Really appreciate the effort, I managed to create a method to merge the JsonObjects. It's working so I think I'll go with it instead. – birgersp Nov 02 '15 at 12:35
  • 1
    Okay, there might be other good ways as you said. you can share your implementation if it worked.Will be helpful for others – Tom Sebastian Nov 02 '15 at 12:40
  • Added the answer, let me know if you think I should rather go with your method. – birgersp Nov 02 '15 at 12:56
  • 1
    I wrote the code not considering JSON - it can be used for any java objects (but little complicated). Your code looks much cleaner,simple and if it satisfies the needs, then stick with your approach. I liked the other way around of using `JsonObject` as the base. – Tom Sebastian Nov 02 '15 at 13:39
1

Managed to create a recursive method for replacing properties in a JsonObject.

private static void mergeObjects(JsonObject object, JsonObject modification) {

    // Iterate through the modified properties
    for (Entry<String, JsonElement> entry : modification.entrySet()) {

        JsonElement je = entry.getValue();

        // If the modified property is an object, iterate through the properties of the modified property
        if (je instanceof JsonObject) {

            JsonObject nextOrigObject = object.get(entry.getKey()).getAsJsonObject();
            JsonObject nextModObject = je.getAsJsonObject();

            mergeObjects(nextOrigObject, nextModObject);

        }

        // If the modified property is not an object, set the original object to match the modified property
        else
            object.add(entry.getKey(), je);

    }

}

With this method, I can merge two objects as follows:

class SomeClass {

    Object var1 = "Hello";
    Object var2 = "AAA";

}

public TestApplication() {

    SomeClass myObject = new SomeClass();
    myObject.var2 = "BBB";

    String modificationString = "{\"var1\":\"Goodbye\"}";

    Gson gson = new Gson();
    JsonObject original = gson.fromJson(gson.toJson(myObject), JsonObject.class);
    JsonObject modification = gson.fromJson(modificationString, JsonObject.class);


    mergeObjects(original, modification);
    myObject = gson.fromJson(original, SomeClass.class);

    System.out.println(myObject.var1); // Prints "Goodbye"

}

public static void main(String[] args) {
    new DummyFile();
}

Comments?

There might be prettier ways to convert the SomeClass object to a JsonObject before the merging, feel free to add your suggestions.

EDIT: added else in merging method

EDIT2: added comments

birgersp
  • 3,909
  • 8
  • 39
  • 79
-1

Any Problem in doing this

    modifed.var2=myObject.var2;
    String modificationStr =  gson.toJson(modifed);
    System.out.println(modificationStr);
Selvakumar Ponnusamy
  • 5,363
  • 7
  • 40
  • 78
  • Are you suggesting I do this for each and every variable of my objects? "myObject" in the example case (the latter one) has 4 json primitives, my application might have hundreds... And how would I determine which variable the "modified" object is supposed to modify? – birgersp Nov 02 '15 at 10:28
  • I meant: *each and every variable of my classes – birgersp Nov 02 '15 at 11:31