1

我已经开始在我的单元和集成测试中使用 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

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

第一个问题是“数字”列表属性将为空。我可以这样处理:

    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 并改用“createPathAndSetValue()”。

但是,这仍然失败,因为即使列表可能存在,它的大小也将为零并且无法访问第 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。

有没有更清洁的方法来做到这一点?

4

2 回答 2

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, ...));

使用像 Guava 的 Lists 类的东西?

于 2018-12-11T12:45:36.400 回答
0

嗯,确实很棘手。一、离题

(奇怪的是他们选择使用基于 1 的索引)

这就是 XPath 的工作方式。围绕原因进行了讨论,但我不确定为什么。Lua 编程语言也使用 1 表示数组。

这是基于您的示例的一些代码(也在GitHub中)。

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);
    }
}

当 Apache Commons JXPath 找到一个集合,更具体地说,java.util.List 的实例时,它将调用 set 方法。无论我们尝试什么。您可以尝试使用工厂的方法,或者使用静态方法调用进行其他黑客攻击....但是如果您的情况足够简单以至于您事先知道列表将包含多少元素,则可以使用上面的方法,即

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

用空元素初始化您的列表,这样List#set(int, E)就不会引发异常。

希望有帮助,布鲁诺

于 2017-06-15T09:43:34.973 回答