我有一个嵌套的对象层次结构,如下所示:
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 :(得分: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升级到更新的版本。如果它有效,您可以将其用作基线,以查看代码中可能存在的不同之处。
答案 1 :(得分: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
参数,而我最终没有任何结果。
我的解决方案是在继续之前复制参数列表。