将JSON反序列化为现有对象(Java)

54
我想知道如何让Jackson JSON库将JSON反序列化为现有对象?我已经尝试了解如何实现此操作,但它似乎只能接受一个Class并自行实例化。
或者,如果不可能,我想知道是否有任何Java JSON反序列化库可以实现。这似乎是C#的相应问题:Overlay data from JSON string to existing object instance。看起来JSON.NET有一个PopulateObject(string,object)函数。

你看过Gson吗?http://code.google.com/p/google-gson/ - yodamad
只是粗略的了解,它能完成上述任务吗? - Jonas N
似乎不行,存在一个Gson问题,编号为"Issue 431: Populate existing object",请参考http://code.google.com/p/google-gson/issues/detail?id=431。 - Jonas N
...而“Gson RoadMap”只有“计划发布:Gson 2.2.3:”和空白。 - Jonas N
看这个方法:https://stackoverflow.com/questions/55426453/how-do-i-deserialize-a-json-string-to-java-object-in-app-engine - Kishore Namala
7个回答

93

谢谢,@boberj - 奇怪的是之前没有人发现这个问题;可能是因为在我提问的时候,Jackson 已经有了这个功能,因为你提供的链接是一个描述旧版本的答案。 - Jonas N
1
但不幸的是,在这种情况下,如果无法解析jsonreadValue将不会抛出异常。有可能恢复正常行为吗? - Hubbitus

1
如果您可以使用另一个库代替Jackson,则可以尝试使用Genson http://owlike.github.io/genson/。除了其他一些不错的功能(如使用非空构造函数进行反序列化而无需任何注释,反序列化为多态类型等),它还支持将JavaBean反序列化为现有实例。以下是一个示例:
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));

如果您有任何问题,请毫不犹豫地使用邮件列表http://groups.google.com/group/genson


谢谢,我一定会看一下的!这是他们的第一个目标,看起来很有前途:尽可能地可扩展,让用户以清晰易懂的方式添加新功能。Genson遵循“我们不能想到每种用例,所以赋予用户以轻松的方式自行完成”的哲学。 - Jonas N
太好了,希望你会喜欢它,事实上我是gensons的作者 =) - eugen

0

flexJson也可以帮助您做同样的事情。

这里是一个从FlexJson Doc复制的示例。

deserializeInto函数接受您的字符串和现有对象的引用。

 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);

请注意,p返回的引用与charlieClone相同,只是值已更新。

0
我使用了Jackson + Spring的DataBinder来实现类似这样的功能。这段代码可以处理数组,但不能处理嵌套对象。
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);
}

0

可以始终加载到虚拟对象中,并使用反射传输数据。如果您坚决要使用gson,则可以这样做。

例如,假设此代码位于您想要复制数据的对象中

    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;
}


0
一种解决方案是解析一个新的对象图/树,然后将其统一复制到现有的对象图/树中。但这当然不太高效,而且更费力,特别是如果具体类型因类型信息的可用性较少而不同。 (所以并不是真正的答案。我希望有更好的答案,只是想避免其他人用这种方式回答。)

0
如果您正在使用Spring框架,可以使用BeanUtils库完成此任务。首先正常反序列化JSON字符串,然后使用BeanUtils将此对象设置在父对象内部。它还期望将对象的变量名称设置在父对象内部。 以下是代码片段:
childObject = gson.fromJson("your json string",class.forName(argType))
BeanUtils.setProperty(mainObject, "childObjectName", childObject);

1
谢谢。恐怕这并不是我想要的。这看起来像是“手动”设置一个字段/属性。 - Jonas N

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接