2017-06-13 37 views
1

我已經開始在我的單元和集成測試中使用Commons JXPath,作爲在Bean層次結構中設置一組屬性的簡單方法。如何讓JXPath乾淨地設置列表/集合條目

我無法「乾淨地」使用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。

有沒有更乾淨的方法來做到這一點?

回答

0

嗯,確實很棘手。首先,題外話

(奇怪的是,他們選擇使用基於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)不會引發異常。

希望有幫助, 布魯諾