2015-09-21 33 views
0

我有一個嵌套的對象層次結構,看起來像這樣:解組列表到一個JavaFX SimpleListProperty

Profile:含有List<Category>

Category包含List<Script>。它通過JavaFX SimpleListProperty公開,以便它可以通過JavaFX的數據綁定綁定。

Script只包含簡單的值。我只是使用JAXB來編組和解組POJO。沒有涉及的數據庫或XML模式。

編組Profile值工作正常,並生成有效的XML。但是,稍後解編相同的XML文件會導致每個Category包含一個空的List<Script>。這似乎是由於Category通過使用JavaFX可綁定屬性來存儲List<Script>

有沒有辦法讓JAXB正確反序列化成一個包含自定義對象的SimpleListProperty?

下面是一個展示相同問題的最小示例。

public class Main 
{ 
    public static void main(String[] args) throws Exception 
    {     
     Script script1 = new Script(); 
     script1.name = "Script 1"; 
     script1.otherData = "Script 1's data"; 

     Script script2 = new Script(); 
     script2.name = "Script 2"; 
     script2.otherData = "Script 2's data"; 

     ArrayList<Script> scriptList = new ArrayList<Script>(); 
     scriptList.add(script1); 
     scriptList.add(script2); 

     Category category1 = new Category(); 
     category1.name = "Category 1";   
     category1.setCategoryScripts(scriptList);   

     Category category2 = new Category(); 
     category2.name = "Category 2"; 
     category2.setCategoryScripts(scriptList);   

     Profile profile = new Profile(); 
     profile.name = "Profile 1"; 
     profile.categories.add(category1); 
     profile.categories.add(category2); 

     JAXBContext context = JAXBContext.newInstance(Profile.class); 
     Marshaller m = context.createMarshaller(); 
     m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     StringWriter xml = new StringWriter(); 
     m.marshal(profile, xml); 

     System.out.println(xml.toString()); 

     Profile deserializedProfile = (Profile)context 
       .createUnmarshaller() 
       .unmarshal(new StringReader(xml.toString()));     

     System.out.println("Profile: " + deserializedProfile.name); 
     for(Category cat : deserializedProfile.categories) 
     { 
      System.out.println("Category: " + cat.name); 
      System.out.println("Scripts:"); 
      for(Script s : cat.getCategoryScripts()) 
      { 
       System.out.printf("\nName: %s, Data: %s", s.name, s.otherData); 
      } 
     } 
    } 
} 

@XmlRootElement 
class Profile 
{ 
    @XmlElement 
    String name; 
    @XmlElementWrapper 
    @XmlElement 
    ArrayList<Category> categories = new ArrayList<Category>(); 
} 

@XmlRootElement 
class Category 
{ 
    @XmlElement 
    String name; 

    ListProperty<Script> categoryScripts = new SimpleListProperty<Script>(); 
    @XmlElementWrapper 
    @XmlElement 
    public final List<Script> getCategoryScripts() { return categoryScripts.get(); } 
    public final void setCategoryScripts(List<Script> value) { categoryScripts.set(FXCollections.observableArrayList(value)); } 
    public ListProperty<Script> categoryScriptProperty() { return categoryScripts; }    
} 

@XmlRootElement 
class Script 
{ 
    @XmlElement 
    String name; 
    @XmlElement 
    String otherData; 
} 

回答

0

一個StackOverflow上的原則是最小的,完整的和可驗證問題。見https://stackoverflow.com/help/mcve

創建此類測試代碼,我這樣做:

public class Test { 
    public static void main(String[] args) throws Exception { 
     JAXBContext jaxbContext = JAXBContext.newInstance(Categories.class); 

     Categories categoriesIn = new Categories(); 
     categoriesIn.scripts.add(new Script("Hello")); 
     categoriesIn.scripts.add(new Script("World")); 

     StringWriter xml = new StringWriter(); 
     jaxbContext.createMarshaller().marshal(categoriesIn, xml); 

     System.out.println(xml.toString()); 

     Categories categoriesOut = (Categories)jaxbContext.createUnmarshaller().unmarshal(new StringReader(xml.toString())); 
     System.out.println(categoriesOut.scripts.size() + " scripts:"); 
     for (Script script : categoriesOut.scripts) 
      System.out.println(" " + script.name); 
    } 
} 
@XmlRootElement 
class Categories { 
    @XmlElementWrapper(name = "Scripts") 
    @XmlElement(name = "Script") 
    List<Script> scripts = new ArrayList<>(); 
} 
class Script { 
    @XmlElement(name = "name") 
    String name; 
    Script() {} 
    Script(String name) { this.name = name; } 
} 

運行它產生:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><categories><Scripts><Script><name>Hello</name></Script><Script><name>World</name></Script></Scripts></categories> 
2 scripts: 
    Hello 
    World 

它似乎做工精細,關於Java 8至少。

試一試。如果它不起作用,那麼您可能需要將Java升級到更新的版本。如果可行,你可以用它作爲基準來查看你的代碼中可能有什麼不同。

+0

注意。我將重新提出這個問題,以便有一個更簡單,可運行的示例來演示相關問題。 〜編輯〜完成。編寫樣本似乎已經解決了這個問題 - 它似乎與使用SimpleListProperty有關。 – PingZing

0

我相信你會遇到JAXB的奇怪現象。請注意,您沒有addCategoryScript方法。那麼JAXB如何將Script對象添加到列表中?

它可以創建它自己的列表,然後給它setCategoryScripts,但它會如何知道要創建什麼樣的列表?

它通過調用getCategoryScripts來獲得初始(空)列表,然後向其中添加元素來解決這個難題。

但是,如果您返回內部列表的副本會發生什麼?
啊哈!清單完成後致電setCategoryScripts

這意味着它將撥打setCategoryScripts並返回getCategoryScripts返回的列表。

我有一些需要特殊處理的實現,所以我所做的就是:

public List<MyObj> getMyList() { 
    return this.myList; 
} 
public void setMyList(List<MyObj> myList) { 
    this.myList.clear(); 
    for (MyObj o : myList) 
     this.myList.add(handle(o)); 
} 

但哎呀。撥打clear實際上清除了傳入的objList參數,並且我什麼也沒有結束。

我的解決方案是在繼續之前複製參數列表。