【问题标题】:How to get JXPath to cleanly set list/collection entries如何让 JXPath 干净地设置列表/集合条目
【发布时间】:2017-06-13 19:35:20
【问题描述】:

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

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

【问题讨论】:

    标签: java jxpath


    【解决方案1】:

    嗯,确实很棘手。一、题外话

    (奇怪的是他们选择使用从 1 开始的索引)

    这就是 XPath 的工作方式。为什么周围有discussions,但我不太确定为什么。 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) 就不会抛出异常。

    希望对您有所帮助, 布鲁诺

    【讨论】:

      【解决方案2】:

      有什么问题:

      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 类?

      【讨论】:

        猜你喜欢
        • 2011-09-14
        • 1970-01-01
        • 2017-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-16
        相关资源
        最近更新 更多