使用Jsonpath获取列表

时间:2016-02-08 12:10:32

标签: java json 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);

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

是否有可能获得预期的结果?

1 个答案:

答案 0 :(得分:4)

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

  1. 解决方案1允许您直接访问errorMsg字段,但是当有多个公司需要手动循环时不能应用
  2. 解决方案2允许构建特定于您的案例的自定义谓词,并且有些通用。
  3. 解决方案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_stackoverflow\\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);
        }
    }