【问题标题】:Using Gson with a path将 Gson 与路径一起使用
【发布时间】:2014-09-28 17:40:29
【问题描述】:

使用简单的 Json 文件,例如:

{"menu": {
  "id": "file",
  "value": "File",
  "popup": {
    "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
    ]
  }
}}

我希望能够使用路径获取名为 menuitemJsonArray

String path =  "menu.popup.menuitem"

我尝试使用:

public static JsonElement fromString(String json, String path) throws JsonSyntaxException {
        JsonObject obj = GsonBuilder.create().fromJson(json, JsonObject.class);
        String[] seg = path.split(".");
        for (String element : seg) {
            if (obj != null) {
                obj = obj.get(element).getAsJsonObject();
            } else {
                return null;
            }
        }
        return obj
}

与:

JsonElement jsonElement = fromString(json, path);

但是当我尝试isJsonArray() 时,返回值是false。当使用Gson.toJson(jsonElement) 进行额外的完整性检查时,输出是最初输入的完整 json 字符串(上图)。 怎么了?

【问题讨论】:

标签: java json gson jsonpath


【解决方案1】:

我不确定为什么这不是 Gson 内置的,但这是我编写的一个方法,它在给定 JsonElement 输入和 JSON 路径的情况下返回 JsonElement

/**
 * Returns a JSON sub-element from the given JsonElement and the given path
 *
 * @param json - a Gson JsonElement
 * @param path - a JSON path, e.g. a.b.c[2].d
 * @return - a sub-element of json according to the given path
 */
public static JsonElement getJsonElement(JsonElement json, String path){

    String[] parts = path.split("\\.|\\[|\\]");
    JsonElement result = json;

    for (String key : parts) {

        key = key.trim();
        if (key.isEmpty())
            continue;

        if (result == null){
            result = JsonNull.INSTANCE;
            break;
        }

        if (result.isJsonObject()){
            result = ((JsonObject)result).get(key);
        }
        else if (result.isJsonArray()){
            int ix = Integer.valueOf(key) - 1;
            result = ((JsonArray)result).get(ix);
        }
        else break;
    }

    return result;
}

要调用它,请使用以下内容:

String jsonString = ...;

Gson gson = new Gson();
JsonObject  jsonObject = gson.fromJson(jsonString, JsonObject.class);
JsonElement subElement = getJsonElement(jsonObject, "a.b.c[2].d";

【讨论】:

【解决方案2】:

split 使用正则表达式来查找应该拆分字符串的位置,但正则表达式中的 . 是表示“行分隔符之外的任何字符”的特殊字符,这意味着您实际上是在每个字符上进行拆分。所以对于像

这样的字符串
"foo"

"foo".split(".") 将拆分为 foo

"foo"
 ^^^

这意味着您将获得包含四个空字符串的结果数组(3 个拆分给出 4 个元素)。

["", "", "", ""]

其实我在这里撒谎是因为split(regex) 做了一件额外的事情:它从结果数组中删除了尾随的空字符串,但是你的数组只包含空字符串,这意味着它们都将被删除,所以split(".") 将只返回空字符串数组[],所以你的循环甚至不会迭代一次(这就是你的方法返回未修改的obj的原因)。

要摆脱这个问题,您需要将. 设为文字(您需要将其转义)。为此,您可以使用例如 split("\\.")split("[.]")split(Pattern.quote(".")split("\\Q.\\E") 工作相同 - 它添加引号区域。

同样在循环内部,您应该首先检查您正在处理的 Json 的类型,因为如果 Json 是数组,getAsJsonObject 将失败。所以你的代码应该看起来像

public static JsonElement fromString(String json, String path)
        throws JsonSyntaxException {
    JsonObject obj = new GsonBuilder().create().fromJson(json, JsonObject.class);
    String[] seg = path.split("\\.");
    for (String element : seg) {
        if (obj != null) {
            JsonElement ele = obj.get(element);
            if (!ele.isJsonObject()) 
                return ele;
            else
                obj = ele.getAsJsonObject();
        } else {
            return null;
        }
    }
    return obj;
}

【讨论】:

  • 或者 path.split("[.]") 也可以用作包围在 [] 中描述文字字符
  • @prash 是的,答案中已经提到了,还有其他一些方法:)
  • 哇,这么详细! ..对不起,我没有注意到我刚刚检查了代码部分.. ty 指出:-)
  • 我尝试使用以下命令运行它:'{"menu":{"id":"file","value":"File","popup":{"menuitem":[{"value ":"New","body":{"services":[{"id":"7d45d915-2e91-4d75-810f-c389339050cb"}]}}]}}}' 试图用 "menu.popup. menuitem”,但它失败了
  • @AviC 为我工作。调用 fromString(json, "menu.popup.menuitem") 返回包含 [{"value":"New","body":{"services":[{"id":"7d45d915-2e91-4d75-810f-c389339050cb"}]}}] 的数组,这似乎与您想要的匹配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-07
  • 1970-01-01
  • 2023-03-12
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多