如何让JXPath干净地设置列表/集合条目

时间:2017-06-13 19:35:21

标签: java jxpath

我已经开始在我的单元和集成测试中使用Commons JXPath,这是在bean层次结构中设置一组属性的简单方法。

我无法使用JXPath"干净利落的情景"正在设置列表条目。

我非常希望JXPath能够做出明显的事情",这样如果我尝试设置条目" [1]"列表(奇怪的是,他们选择使用基于1的索引),它将配置基础列表至少有一个条目。从我所见,这不会发生,我必须手动完成。

例如,这里有一些我想写的代码:

    JXPathContext   context = JXPathContext.newContext(request);
    context.setValue("foo", new Foo());
    context.setValue("foo/subscriberNumber", "1234567890");
    context.setValue("foo/bar", new Bar());
    context.setValue("foo/bar/numbers[1]", "123"); // this fails

由于两个不同的原因,这不起作用。

第一个问题是"数字" list属性为null。我可以像这样处理:

    JXPathContext   context = JXPathContext.newContext(request);
    context.setValue("foo", new Foo());
    context.setValue("foo/subscriberNumber", "1234567890");
    context.setValue("foo/bar", new Bar());
    context.setValue("foo/bar/numbers", new ArrayList<String>());
    context.setValue("foo/bar/numbers[1]", "123");

或者我可以注册一个AbstractFactory并使用&#34; createPathAndSetValue()&#34;代替。

然而,这仍然失败,因为即使列表可能存在,它也将没有大小并且无法访问第0个条目。

我被迫做这样的事情:

    JXPathContext   context = JXPathContext.newContext(request);
    context.setValue("foo", new Foo());
    context.setValue("foo/subscriberNumber", "1234567890");
    context.setValue("foo/bar", new Bar());
    context.setValue("foo/bar/numbers", Arrays.asList(""));
    context.setValue("foo/bar/numbers[1]", "123");

如果我必须在列表中设置除第一个条目之外的其他条目,我将不得不使用更多虚拟条目来初始化列表。

我也有责任在每个地方添加评论以平息WTF。

有更简洁的方法吗?

2 个答案:

答案 0 :(得分:0)

Hmmm, tricky indeed. First, digressing

(odd that they chose to use 1-based indexes)

That's the way XPath works. There are discussions around why, but I'm not really sure why. Lua programming language also uses 1 for arrays.

Here's some code (also in GitHub) based on your example.

public class TestsJxpath {

    public static class Request {
        private Foo foo;
        public Foo getFoo() {
            return foo;
        }
        public void setFoo(Foo foo) {
            this.foo = foo;
        }
        @Override
        public String toString() {
            return "Request [foo=" + foo + "]";
        }
    }

    public static class Foo {
        private String subscriberNumber;
        private Bar bar;
        public String getSubscriberNumber() {
            return subscriberNumber;
        }
        public void setSubscriberNumber(String subscriberNumber) {
            this.subscriberNumber = subscriberNumber;
        }
        public Bar getBar() {
            return bar;
        }
        public void setBar(Bar bar) {
            this.bar = bar;
        }
        @Override
        public String toString() {
            return "Foo [subscriberNumber=" + subscriberNumber + ", bar=" + bar + "]";
        }
    }

    public static class Bar {
        private List<String> numbers = Arrays.asList("");
        public List<String> getNumbers() {
            return numbers;
        }
        @Override
        public String toString() {
            return "Bar [numbers=" + numbers + "]";
        }
    }

    // added after OP reported on
    // https://stackoverflow.com/questions/44530112/how-to-get-jxpath-to-cleanly-set-list-collection-entries
    public static void main(String[] args) {
        Request request = new Request();
        JXPathContext context = JXPathContext.newContext(request);
        context.setValue("foo", new Foo());
        context.setValue("foo/subscriberNumber", "1234567890");
        context.setValue("foo/bar", new Bar());
        context.setValue("foo/bar/numbers[1]", "123"); // this no longer fails
        System.out.println(request);
    }

    public static void main2(String[] args) {
        Phone p1 = new Phone(1);
        Phone p2 = new Phone(2);
        List<Phone> phones = new ArrayList<>();
        phones.add(p1);
        phones.add(p2);

        JXPathContext ctx = JXPathContext.newContext(phones);
        System.out.println(phones);
        long phone1 = (long) ctx.getValue("/*[type='br.eti.kinoshita.commons.jxpath.Phone']");
        System.out.println(phone1);
    }
}

When Apache Commons JXPath finds a collection, more specifically, instance of java.util.List, then it will call the set method. No matter what we try. You could try a way around with a factory, or maybe other hacks with static method calls.... but if your case is simple enough that you know beforehand how many elements your list will contain, you could use the approach above, i.e.

private List<String> numbers = Arrays.asList("");

Initialize your list with empty elements, so that List#set(int, E) doesn't throw an exception.

Hope that helps, Bruno

答案 1 :(得分:0)

怎么了?

JXPathContext   context = JXPathContext.newContext(request);
context.setValue("foo", new Foo());
context.setValue("foo/subscriberNumber", "1234567890");
context.setValue("foo/bar", new Bar());
context.setValue("foo/bar/numbers", Lists.newArrayList(null, 123, null, ...));

使用类似番石榴的Lists类的东西吗?