在JSONPath中使用filter时如何获取第一个元素?

18

所以我正在处理以下json:

{
   "id": "",
   "owner": "some dude",
   "metaData": {
      "request": {
         "ref": null,
         "contacts":[
            {
               "email": null,
               "name": null,
               "contactType": "R"
            },
            {
               "email": null,
               "name": "Dante",
               "contactType": "S"
            }
         ]
      }
   }
}

我想要检索类型为S的联系人的name,并且只返回第一个结果。

使用此路径"$..contacts[?(@.contactType == 'S')].name"的jsonpath总是返回字符串数组,因为过滤操作始终以数组形式返回结果。

所以我尝试了"$..contacts[?(@.contactType == 'S')].name[0]""$..contacts[?(@.contactType == 'S')][0].name",但没有成功。这些路径返回空结果。

所以我的问题是,在使用jsonpath时是否有方法仅获取过滤后的第一个元素。我当前正在使用jayway jsonpath v2.2.0。


1
从2.2.0版本开始,您不能这样做。请参见以下未解决的问题:https://github.com/jayway/JsonPath/issues/272 - approxiblue
3个回答

3
我找到的最快解决方案就是将其与列表进行比较,就像在您的场景中一样:

我找到的最快解决方案就是将其与列表进行比较,就像在您的场景中一样:

.andExpect(jsonPath("$..contacts[?(@.contactType == 'S')].name", equalTo(Arrays.asList("Dante"))))

(假设你当然正在尝试比较测试结果)

1
如果您使用spring-test中的MockMvc类与jsonpath一起使用,则可以编写以下虚拟匹配器:
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.core.IsEqual;

import net.minidev.json.JSONArray;

public class FirstMatcher<T> extends BaseMatcher<T> {

    private final Matcher<?> matcher;

    public static <T> FirstMatcher<T> matcher(Matcher<T> matcher) {
        return new FirstMatcher<T>(matcher);
    }

    public static FirstMatcher<Object> value(Object value) {
        return new FirstMatcher<Object>(value);
    }

    public FirstMatcher(Matcher<T> matcher) {
        this.matcher = matcher;
    }

    public FirstMatcher(Object value) {
        this.matcher = new IsEqual<Object>(value); 
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("first matcher");
    }

    @Override
    public boolean matches(Object item) {
        if (!(item instanceof JSONArray)) {
            return false;
        }

        JSONArray array = (JSONArray)item;
        if (array.isEmpty()) {
            return false;
        }

        Object obj = array.get(0);
        return matcher.matches(obj);
    }

}

使用以下方式:
mockMvc.
    perform(get(url).
            accept(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_PLAIN).accept(MediaType.ALL)).
    andExpect(status().isOk()).
    andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8)).
    andExpect(jsonPath("$.properties[?(@.propertyName == 'name1')].description").value(FirstMatcher.matcher(IsNull.nullValue()))).
    andExpect(jsonPath("$.properties[?(@.propertyName == 'name2')].required").value(FirstMatcher.value(true)));

P.S. 由于 net.minidev.json.JSONArray 是 java.util.List 的子类,因此可以将其转换为 List 或甚至 Iterable,而不是 net.minidev.json.JSONArray。 :)


0

很遗憾,如果使用Jayway JsonPath实现,这是不可能的。您需要手动将结果解析为列表,然后进行必要的操作。

对于使用Spring Boot和WebTestClient进行测试的人员,可以执行以下操作:

webTestClient.get().uri("/path").exchange().expectBody().jsonPath("$.dogs[?(@.name == 'Jhon')].pawns").value(value -> {
    var list = (JSONArray) value;
    if (!list.get(0).toString().contains("4")) {
        Assertions.fail("Jhon does not has 4 pawns");
    }
});

这个想法就是调用value方法,它允许你从jsonpath过滤器中获取结果,然后你可以手动检查值。


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