Deserialize JSON into existing object (Java)
Asked Answered
E

7

54

I'd like to know how one might get the Jackson JSON library to deserialize JSON into an existing object? I've tried to find how to to this; but it seems to only be able to take a Class and instantiate it itself.

Or if not possible, I'd like to know if any Java JSON deserialization libraries can do it.

This seems to be a corresponding question for C#: Overlay data from JSON string to existing object instance. It seems JSON.NET has a PopulateObject(string,object).

Emlin answered 20/9, 2012 at 18:25 Comment(5)
Have you had a look at Gson ? code.google.com/p/google-gsonAwakening
Only cursory. Can it do the above?Emlin
Seems it can't, there is a Gson issue "Issue 431: Populate existing object" at code.google.com/p/google-gson/issues/detail?id=431Emlin
...and the "Gson RoadMap" only has "Planned Releases: Gson 2.2.3: " and empty space.Emlin
Looks at this approach #55426953Cockleshell
L
94

You can do this using Jackson:

mapper.readerForUpdating(object).readValue(json);

See also Merging Two JSON Documents Using Jackson

Louettalough answered 22/11, 2013 at 20:19 Comment(2)
Thanks, @boberj - Odd that no-one managed to find this before; the feature must've been in Jackson at the time I asked, since your link is to an older answer describing this.Emlin
But unfortunately in that case readValue will not throw exceptions if can't parse json. Is it possible revert it to normal behavior?Lossa
B
1

If you can use another library instead of Jackson you can try Genson http://owlike.github.io/genson/. In addition of some other nice features (such as deserialize using a non empty constructor without any annotation, deserialize to polymorphic types, etc) it supports deserialization of JavaBean into an existing instance. Here is an example:

BeanDescriptorProvider provider = new Genson().getBeanDescriptorFactory();
BeanDescriptor<MyClass> descriptor = provider.provide(MyClass.class, genson);
ObjectReader reader = new JsonReader(jsonString);
MyClass existingObject = descriptor.deserialize(existingObject, reader, new Context(genson));

If you have any question don't hesitate to use its mailing list http://groups.google.com/group/genson.

Balliol answered 20/9, 2012 at 19:17 Comment(2)
Thanks, I'll definitely take a look! Here's their first goal, and it looks promising: Be as much extensible as possible by allowing users to add new functionnalities in a clean and easy way. Genson applies the philosophy that "We can not think of every use case, so give to users the ability to do it by them self in a easy way".Emlin
Great, I hope you will like it, in fact I am gensons author =)Balliol
E
0

One solution is to parse a new object graph/tree and then unify-copy into the existing object graph/tree. But that's of course less efficient, and more work, especially if concrete types differ because of less availability of type information. (So not really an answer. I hope there's a better answer, just want to avoid others answering in this way.)

Emlin answered 20/9, 2012 at 18:25 Comment(0)
T
0

flexJson can also help you do the same.

Here is an example copied from FlexJson Doc

The deserializeInto function takes your string and reference to existing object.

 Person charlie = new Person("Charlie", "Hubbard", cal.getTime(), home, work );
 Person charlieClone = new Person( "Chauncy", "Beauregard", null, null, null );
 Phone fakePhone = new Phone( PhoneNumberType.MOBILE, "303 555 1234");
 charlieClone.getPhones().add( fakePhone ); 
 String json = new JSONSerializer().include("hobbies").exclude("firstname", "lastname").serialize( charlie ); 
 Person p = new JSONDeserializer<Person>().deserializeInto(json, charlieClone);

Note that the reference returned in p is same as charlieClone just with updated values.

Tenrec answered 12/10, 2012 at 12:34 Comment(0)
K
0

I used Jackson + Spring's DataBinder to accomplish something like this. This code handles arrays but not nested objects.

private void bindJSONToObject(Object obj, String json) throws IOException, JsonProcessingException {
    MutablePropertyValues mpv = new MutablePropertyValues();
    JsonNode rootNode = new ObjectMapper().readTree(json);
    for (Iterator<Entry<String, JsonNode>> iter = rootNode.getFields(); iter.hasNext(); ) {
        Entry<String, JsonNode> entry = iter.next();
        String name = entry.getKey();
        JsonNode node = entry.getValue();
        if (node.isArray()) {
            List<String> values = new ArrayList<String>();
            for (JsonNode elem : node) {
                values.add(elem.getTextValue());
            }
            mpv.addPropertyValue(name, values);
            if (logger.isDebugEnabled()) {
                logger.debug(name + "=" + ArrayUtils.toString(values));
            }
        }
        else {
            mpv.addPropertyValue(name, node.getTextValue());
            if (logger.isDebugEnabled()) {
                logger.debug(name + "=" + node.getTextValue());
            }
        } 
    }
    DataBinder dataBinder = new DataBinder(obj);
    dataBinder.bind(mpv);
}
Kappel answered 9/5, 2013 at 18:23 Comment(0)
C
0

could always load into a dummy object and use reflection to transfer the data. if your heart is set on just using gson

example. assuming this code is in the object you want to copy data into

    public void loadObject(){
Gson gson = new Gson();
//make temp object
YourObject tempStorage = (YourObject) gson.fromJson(new FileReader(theJsonFile), YourObject.class);
//get the fields for that class
ArrayList<Field> tempFields = new ArrayList<Field>();
ArrayList<Field> ourFields = new ArrayList<Field>();
getAllFields(tempFields, tempStorage.getClass());
getAllFields(thisObjectsFields, this.getClass());
for(Field f1 : tempFields){
    for(Field f2 : thisObjectsFields){
        //find matching fields
        if(f1.getName().equals(f2.getName()) && f1.getType().equals(f2.getType())){
            //transient and statics dont get serialized and deserialized.
            if(!Modifier.isTransient(f1.getModifiers())&&!Modifier.isStatic(f1.getModifiers())){
                //make sure its a loadable thing
                f2.set(this, f1.get(tempStorage));
            }
        }
    }
}

}

public static List<Field> getAllFields(List<Field> fields, Class<?> type) {
    for (Field field : type.getDeclaredFields()) {
        fields.add(field);
    }
    if (type.getSuperclass() != null) {
        fields = getAllFields(fields, type.getSuperclass());
    }
    return fields;
}

Chuckle answered 16/9, 2014 at 20:44 Comment(0)
U
0

If you are using spring framework you can use BeanUtils library for this task. First deserialize your json String normally and then use BeanUtils to set this object inside a parent object. It also expects the variable name of the object to be set inside the parent object. Here is the code snippet:

childObject = gson.fromJson("your json string",class.forName(argType))
BeanUtils.setProperty(mainObject, "childObjectName", childObject);
Unamerican answered 6/8, 2015 at 7:25 Comment(1)
Thanks. I am afraid that this doesn't really do what I intended. This looks like 'manually' setting a field/property.Emlin

© 2022 - 2024 — McMap. All rights reserved.