使用 Jsonpath 获取列表

Get List by using Jsonpath

我正在使用 Jayway JsonPath 2.1.0 使用 JsonPath 解析 JSON 字符串。

JSON结构如下:

{
    "status": "",
    "source": "",
    "processedRecords": 1,
    "totalRecords": 4,
    "description": "1 company(ies) added/updated, and 2 company(ies) failed",
    "payload": [{
        "Added/updated company(ies)": [
            "COM001FILE"
        ]
    }, {
        "Failed company(ies)": [{
            "id": "COM003FILE",
            "index": "NA",
            "errorMsg": [
                [
                    "INVALID_LOCATION"
                ]
            ]
        }, {
            "id": "COM002FILE",
            "index": "NA",
            "errorMsg": [
                [
                    "INACTIVE"
                ],
                [
                    "INVALID_LOCATION",
                    "INACTIVE"
                ]
            ]
        }]
    }]
}

现在的需求是获取errorMsg包含INACTIVE的id列表。为此,我使用了如下所示的过滤器。

Filter resposneFilter = Filter.filter(Criteria.where("errorMsg").is(
            "INACTIVE"));
List<Map<String, Object>> respons = JsonPath.parse(response).read(
            "$.payload[*]", resposneFilter);

但作为输出,我得到了有效载荷中的所有值。

是否可以得到预期的结果?

考虑到您提供的 JSON 并阅读了 Jayway JsonPath 的文档。我找到了两个可以帮助您解决问题的解决方案。

  1. 解决方案1可以直接访问errorMsg字段,但是当有多个公司时不适用,因为你必须手动循环
  2. 解决方案 2 允许构造特定于您的情况并且有些通用的自定义谓词。

解决方案 1 您的过滤器似乎是正确的,如下所示:

Filter responseFilter = Filter.filter(Criteria.where("errorMsg").is("INACTIVE"));

查看您的 JSON,有效负载可以被认为是起点,结构似乎是固定的 payload 数组的第二个值是失败的公司,这就是您感兴趣的因此,我会写 json 路径如下:

Map<String, Object> inactiveFailedCompany =  
           parse(json).read("$.payload[1].['Failed company(ies)'][1]", responseFilter);

如果您在上面注意到,我已经指定了 payload[1] 并且我假设这就是您的 JSON 的结构。我还指定了['Failed company(ies)'][1],那就是访问有INACTIVE的公司。您显然可以将此解决方案与循环一起使用,以循环遍历 ['Failed company(ies)'][1] 的索引并按如下方式打印 ID:

System.out.println("Company Object > " + inactiveFailedCompany.toString());
//output: Output > {id=COM002FILE, index=NA, errorMsg=[["INACTIVE"],["INVALID_LOCATION","INACTIVE"]]}

System.out.println("Company ID > " + inactiveFailedCompany.get("id"));
//output: COM002FILE

看到上面的解决方案,我觉得不好,因为它不够好,所以我阅读了更多文档并遇到了Roll your own predicate因此解决方案2。

解决方案 2

正如文档中所解释的,我定制了谓词的 apply 方法以满足您的要求,解决方案如下:

Predicate inactiveCompaniesPredicate = new Predicate() {
            @Override
            public boolean apply(PredicateContext ctx) {
                if(ctx.item(Map.class).containsKey("errorMsg") && 
                    ((JSONArray)((JSONArray) ctx.item(Map.class).get("errorMsg")).get(0)).get(0).equals("INACTIVE")) {
                    return true; 
                } else {
                    return false; 
                }
            }
        };

现在使用上面的谓词如下:

List<Map<String, Object>> failedCompanies = 
           parse(json).read("$.payload[1].['Failed company(ies)'][?]", List.class, inactiveCompaniesPredicate);

然后循环遍历公司列表并打印失败公司的ID,如下所示:

for(Map<String, Object> object: failedCompanies) {
    System.out.println(object.get("id"));
    //output: COM002FILE
}

我想反思谓词中可怕的 if 条件,如下所示:

if 条件的左边部分 - xx

这部分过滤所有包含errorMsg字段的。

if(ctx.item(Map.class).containsKey("errorMsg") && yy)

右部分if条件-yy

这部分确保 errorMsgINACTIVE

if(xx & ((JSONArray)((JSONArray) ctx.item(Map.class).get("errorMsg")).get(0)).get(0).equals("INACTIVE"))

这是我用来测试的示例代码(我将 Jsonpath 2.1 及其依赖项作为外部 jar 导入到我的 eclipse 中)

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import net.minidev.json.JSONArray;
import com.jayway.jsonpath.Predicate;
import static com.jayway.jsonpath.JsonPath.parse;

public class JaywayJSON {
    public static void main(String[] args) throws IOException {
        String json = readFile("C:\test_Whosebug\jayway.json",
                Charset.defaultCharset());

        /*
         * //Solution 1
         * 
         * Filter responseFilter =
         * Filter.filter(Criteria.where("errorMsg").is("INACTIVE"));
         * 
         * Map<String, Object> inactiveFailedCompany =
         * parse(json).read("$.payload[1].['Failed company(ies)'][1]",
         * responseFilter);
         * 
         * System.out.println("Company Object > " +
         * inactiveFailedCompany.toString()); //output: Output > {id=COM002FILE,
         * index=NA, errorMsg=[["INACTIVE"],["INVALID_LOCATION","INACTIVE"]]}
         * 
         * System.out.println("Company ID > " +
         * inactiveFailedCompany.get("id")); //output: COM002FILE
         */

        // solution 2
        Predicate inactiveCompaniesPredicate = new Predicate() {
            @Override
            public boolean apply(PredicateContext ctx) {
                if (ctx.item(Map.class).containsKey("errorMsg")
                        && ((JSONArray) ((JSONArray) ctx.item(Map.class).get(
                                "errorMsg")).get(0)).get(0).equals("INACTIVE")) {
                    return true;
                } else {
                    return false;
                }
            }
        };

        List<Map<String, Object>> failedCompanies = parse(json).read(
                "$.payload[1].['Failed company(ies)'][?]", List.class,
                inactiveCompaniesPredicate);

        for (Map<String, Object> object : failedCompanies) {
            System.out.println(object.get("id"));
        }
    }

    public static String readFile(String path, Charset encoding)
            throws IOException {
        byte[] encoded = Files.readAllBytes(Paths.get(path));
        return new String(encoded, encoding);
    }
}