动态JSON结构到Java结构

时间:2016-11-03 16:55:32

标签: java json

我正在开发一个项目,使用JSON作为创建Java对象的配置框架。这也是我第一个来自CF / PHP / JS等多年经验的专业Java项目......

我在将JSON转换为Java时可以找到的每个资源都基于这样的想法,即您必须首先在Java中手动构建对象,POJO,然后使用JSON填充它。

作为一名网络语言老手,我对这个想法感到窒息。我认为编译语言的播放方式不同,但我认为这是从命令行到机器语言共享的原则开发人员:“如果你必须做两次以上,那就自动完成它。”

......然而看起来主流的Java智慧似乎是:每次都手工制作繁琐的结构,然后使用GSON / Jackson /无论如何填充刚性结构。如果你的JSON发生了变化(并且它会因为JSON而重新完成)。

是否与网络代码的思维方式相似? 1)加载JSON
2)基于JSON结构构建本地语言结构 3)用结构中的数据填充新对象 4)按照你的意愿使用物体。

(正如@Thomas指出的那样,为什么这是必要的最准确的场景是返回JSON的API响应,它可能会定期不同,虽然对于高级语言,鸭子打字通常不舒服,甚至将一切转换为与每次重建框架相比,一个字符串可以节省工作量和时间{或者有一个工具可以将你的JSON从无数来源转换为可供任何其他工具使用}。同样,从牛仔语言的角度来看,这似乎很明显,但对于高级语言人们不理解这个问题。)

1 个答案:

答案 0 :(得分:2)

我仔细阅读了上面的评论,我认为有一个非常常见的情况是,即使在编译时已知API结构, ad hoc Java对象也非常有用。

让我们假设您处理的API返回一个复杂的JSON对象,您只对非常具体的属性值感兴趣。

以下面的谷歌地图API调用为例,获取地理位置详细信息:

https://maps.googleapis.com/maps/api/geocode/json?address=sunnyvale,CA

正如您可以查看的那样,响应结构非常复杂。假设您只想获取latlng属性值,应该有一种更简单的方法,而不是预定义完整的相应Java类。

所以,是的,如果您不想使用强类型预定义Java类,则需要使用原始对象类型和地图。更具体地说,是一个StringMap。

现在最后我可以给出一个解决方案:

理想情况下,您希望使用与JS中相同的本机表示法访问属性。像这样:

String url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address;
String responseStr = fetch(url);
JsonHelper response =  JsonHelper.forString(responseStr);

String status = (String) response.getValue("status");
if(status != null && status.equals("OK")) {
   lat = (Double) response.getValue("results[0].geometry.location.lat");        
   lng = (Double) response.getValue("results[0].geometry.location.lng");
}

以下JsonHelper类代码来自jello-framework,它可以让您做到这一点。

package jello.common;

import java.util.List;

import com.google.gson.Gson;
import java.util.AbstractMap;

public class JsonHelper {

    private Object json;

    public JsonHelper(String jsonString) {
        Gson g = new Gson();
        json = g.fromJson(jsonString, Object.class);
    }

    public static JsonHelper forString(String jsonString) {
        return new JsonHelper(jsonString);
    }

    @SuppressWarnings("unchecked")
    public Object getValue(String path) {
        Object value = json;
        String [] elements = path.split("\\.");
        for(String element : elements) {
            String ename = element.split("\\[")[0];

            if(AbstractMap.class.isAssignableFrom(value.getClass())) {
                value = ( (AbstractMap<String, Object>) value).get(ename);

                if(element.contains("[")) {
                    if(List.class.isAssignableFrom(value.getClass())) {
                        Integer index = Integer.valueOf(element.substring(element.indexOf("[")+1, element.indexOf("]")) );
                        value = ((List<Object>) value).get(index);
                    }
                    else {
                        return null;
                    }
                }
            }
            else {
                return null;
            }
        }

        return value;
    }
}