I have a large XML file that consists of many events. I would like to unmarshal them. As it's a large file, I would like to unmarshal them one by one so the whole file is not stored in memory. It works for some events but fails for some due to the fact that it's unable to map to a particular class as it's already in the next event.
Note: I am aware of the XMLEventReader but most of them have mentioned it as not very memory efficient so I am trying to use XMLStreamReader and accomplish this.
Following is the sample XML file that contains the events:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<extension>
<extension>
<c>
<name>CName</name>
<age>CAge</age>
</c>
</extension>
</extension>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
<a>
<name>AName</name>
<age>AAge</age>
</a>
<extension>
<b>
<name>BName</name>
<age>BAge</age>
</b>
</extension>
I have 3 classes corresponding to them which will be used for unmarshalling:
#XmlRootElement(name = "a")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "a", propOrder = {"name","age"})
public class A
{
private String name;
private String age;
//Getter, Setter and other constructors
}
#XmlRootElement(name = "extension")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "extension", propOrder = {"name","age"})
public class B
{
#XmlPath("b/name/text()")
private String name;
#XmlPath("b/age/text()")
private String age;
//Getter, Setter and other constructors
}
#XmlRootElement(name = "extension")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "extension", propOrder = {"name","age"})
public class C
{
#XmlPath("extension/c/name/text()")
private String name;
#XmlPath("extension/c/age/text()")
private String age;
//Getter, Setter and other constructors
}
Following is my Main class which will be used for unmarshalling:
public class Main{
private Unmarshaller unmarshaller = null;
private JAXBContext jaxbContext = null;
public void unmarshaller(InputStream xmlStream) throws IOException, XMLStreamException, JAXBException {
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(xmlStream);
//Navigate to next and start of the XML Elements
streamReader.next();
//Read Until the end of the file
while (streamReader.hasNext()) {
//Check if the element is "extension" if so its Class B or C
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//Check if the next element also has "extension" if so its Class C
//This is IMPORTANT step for mapping b/w Class B & C which is confusing me
streamReader.next();
if (streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("extension")) {
//If there is 2 extension tag then its Class C
classSpecifier(C.class);
final C cInfo = unmarshaller.unmarshal(streamReader, C.class).getValue();
System.out.println(cInfo);
}else{
//If there is no "extension" tag then its Class B
//THIS IS WHERE ITS FAILING: IF ITS NOT CLASS C THEN IT WOULD COME HERE BUT SINCE I HAVE
//ALREADY MOVED TO NEXT ELEMENT TO CHECK IF ITS "extension" ITS UNABLE TO MAP THE WHOLE CLASS TO CLASS B
classSpecifier(B.class);
final B bInfo = unmarshaller.unmarshal(streamReader, B.class).getValue();
System.out.println(bInfo);
}
}else if(streamReader.isStartElement() && streamReader.getLocalName().equalsIgnoreCase("a")){
//If there is no "extension" then its class A
classSpecifier(A.class);
final A aInfo = unmarshaller.unmarshal(streamReader, A.class).getValue();
System.out.println(aInfo);
}
}
}
//Method to initialize the JAXBContext and Unmarshaller based on the incoming eventType
private void classSpecifier(Class eventTypeClass) throws JAXBException {
this.jaxbContext = JAXBContext.newInstance(eventTypeClass);
unmarshaller = jaxbContext.createUnmarshaller();
}
public static void main(String args[]){
try{
InputStream xmlStream = Main.class.getClassLoader().getResourceAsStream("InputEPCISEvents.xml");
unmarshaller(xmlStream);
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
}
}
The problem I am facing is the differentiating between class B and C.
I need to check if the incoming localName is extension.
If it's extension then I need to check if the next element localName is also extension.
If so then it's class C if not then class B.
Since in Step-2 I have already moved to streamreader.next() and if the element is not extension then its unable to map it to class B as I have already moved to next() element and it does not have the whole class.
I am looking for some solutions where I can do the following:
If the element in the 2nd verification is not extension then go back to the previous element then assign the whole class to class B.
Assign the streamReader to tempreader when making a check so that you will be advancing in tempreader. But this also failing.
Is there a way to go back to the previous element in a stream or else how can I tackle this issue? I hope I was able to provide a complete explanation.
"Going back" in a stream implies some kind of memory, so there is no point in sticking to the most memory-efficient tool.
XMLEventReader can handle this with ease:
public class Main {
public static void main(String args[]) throws Exception {
Unmarshaller aUnmarshaller = JAXBContext.newInstance(A.class).createUnmarshaller();
Unmarshaller bUnmarshaller = JAXBContext.newInstance(B.class).createUnmarshaller();
Unmarshaller cUnmarshaller = JAXBContext.newInstance(C.class).createUnmarshaller();
try (InputStream input = Main.class.getResourceAsStream("InputEPCISEvents.xml")) {
XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(input);
while (reader.hasNext()) {
XMLEvent event = reader.peek();
if (event.isStartElement()) {
switch (event.asStartElement().getName().getLocalPart()) {
case "a" -> System.out.println(aUnmarshaller.unmarshal(reader));
case "b" -> System.out.println(bUnmarshaller.unmarshal(reader));
case "c" -> System.out.println(cUnmarshaller.unmarshal(reader));
}
}
reader.next();
}
}
}
#XmlAccessorType(XmlAccessType.FIELD)
static class ABC {
String name;
String age;
public String toString() {
return getClass().getSimpleName() + "{name='" + name + "', age='" + age + "}";
}
}
#XmlRootElement static class A extends ABC {}
#XmlRootElement static class B extends ABC {}
#XmlRootElement static class C extends ABC {}
}
Output:
C{name='CName', age='CAge}
B{name='BName', age='BAge}
A{name='AName', age='AAge}
B{name='BName', age='BAge}
By the way, your XML needs to be wrapped in a parent element as it contains more than one root element.
Related
I consume a webservice and I'm receiving an XML response with a parent and a child node with the same name. The problem is that the last hierarchie has no values.
From my point of view JAXB should handle a List TestDetails.
Class Envelope:
#XmlRootElement(name="Envelope", namespace="http://schemas.xmlsoap.org/soap/envelope/")
#XmlAccessorType(XmlAccessType.FIELD)
public class Envelope {
#XmlElement(name="Body", namespace="http://schemas.xmlsoap.org/soap/envelope/")
private Body Body;
}
Class Body:
#XmlAccessorType(XmlAccessType.FIELD)
public class Body {
#XmlElement(name="GetTestlistWithConnectionsResponse", namespace="http://tempuri.org/")
private GetTestlistWithConnectionsResponse GetTestlistWithConnectionsResponse;
public Body() {}
}
Class GetTestlistWithConnectionsResponse:
#XmlAccessorType(XmlAccessType.FIELD)
public class GetTestlistWithConnectionsResponse {
public GetTestlistWithConnectionsResponse() {}
#XmlElement(name="GetTestlistWithConnectionsResult",
namespace="http://tempuri.org/")
private GetTestlistWithConnectionsResult GetTestlistWithConnectionsResult;
}
Class GetTestlistWithConnectionsResult:
#XmlAccessorType(XmlAccessType.FIELD)
public class GetTestlistWithConnectionsResult {
public GetTestlistWithConnectionsResult() {}
#XmlElement(name="TestDetails", namespace="http://schemas.datacontract.org/XXX")
private TestDetails TestDetails ;
}
Class TestDetails:
#XmlAccessorType(XmlAccessType.FIELD)
public class TestDetails{
public TestDetails() {}
#XmlElement(name="A", namespace="http://schemas.datacontract.org/XXX")
private String A;
#XmlElement(name="B", namespace="http://schemas.datacontract.org/XXX")
private String B;
#XmlElement(name="TestDetails")
private List<TestDetails> TestDetails = new ArrayList<TestDetails>();
}
XML Structure:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<GetTestlistWithConnectionsResponse xmlns="http://tempuri.org/">
<GetTestlistWithConnectionsResult xmlns:a="http://schemas.datacontract.org/XXX" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Error i:nil="true"/>
<a:TestDetails>
<a:TestDetails>
<a:A>A</a:A>
<a:B>B</a:B>
</a:TestDetails>
</a:TestDetails>
</GetTestlistWithConnectionsResult>
</GetTestlistWithConnectionsResponse>
</s:Body>
</s:Envelope>
Unmarshall Method:
public Envelope unmarshallFromFile(){
Envelope testDetail= null;
try {
JAXBContext jaxbContext = JAXBContext.newInstance(Envelope.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
InputStream inStream = null;
try {
inStream = new FileInputStream(this.fileLoc);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
flightDetailsSN = (Envelope) jaxbUnmarshaller.unmarshal( inStream );
} catch (JAXBException e) {
e.printStackTrace();
}
return testDetail;
}
When I invoke my unmarshall method I receive an object with a:TestDetails Item with an empty list. I was expecting that the list contains one element with values A and B.
A and B in your XML are elements, not attributes. Try changing
#XmlAttribute
private String A;
private String B;
to
#XmlElement(name = "A")
private String a;
#XmlElement(name = "B")
private String b;
Try out this, by changing sub element or child name if you have no problem. (I think its because of same name for header and sub element.)
<a:TestDetails>
<a:TestDetail>
<a:A>A</a:A>
<a:B>B</a:B>
</a:TestDetail>
</a:TestDetails>
I will try to be as clear as I can. This question was inspired from a previously asked question from years ago.
I'd like to to encapsulate xml data into a single Root element object.
So for the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FosterHome>
<Orphanage>Happy Days Daycare</Orphanage>
<Location>Apple Street</Location>
<Families>
<Family>
<ParentID>Adams</ParentID>
<ChildList>
<ChildID>Child1</ChildID>
<ChildID>Child2</ChildID>
</ChildList>
</Family>
<Family>
<ParentID>Adams</ParentID>
<ChildList>
<ChildID>Child3</ChildID>
<ChildID>Child4</ChildID>
</ChildList>
</Family>
</Families>
<RemainingChildList>
<ChildID>Child5</ChildID>
<ChildID>Child6</ChildID>
</RemainingChildList>
</FosterHome>
I have the following JAXB annotated classes.
FosterHome.java
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="FosterHome")
#XmlAccessorType(XmlAccessType.FIELD)
public class FosterHome {
#XmlElement(name="Orphanage")
private String orphanage;
#XmlElement(name="Location")
private String location;
#XmlElementWrapper(name="Families")
#XmlElement(name="Family")
private List<Family> families;
#XmlElementWrapper(name="RemainingChildList")
#XmlElement(name="ChildID")
private List<String> remainingChildren;
}
Family.java
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Family {
#XmlElement(name="ParentID")
private String parentID;
#XmlElementWrapper(name="ChildList")
#XmlElement(name="ChildID")
private List<String> childList;
}
Demo.java
public static FosterHome parseXML(File file) {
try {
JAXBContext context = JAXBContext.newInstance(FosterHome.class);
Unmarshaller um = context.createUnmarshaller();
FosterHome fosterHome = (FosterHome) um.unmarshal(file);
for(Family family : fosterHome.getFamilies()) {
for(String childID : family.getChildList()) {
System.out.println(childID);
// my question is here
// How to encapsulate all the data into the fosterHome object
// such as:
// FosterHome fh = childID.getChildID();
}
}
return fosterHome;
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
FosterHome fh = parseXML(new File("./testdata/jaxbtest.xml"));
sysout(fh); // print out all the data from the xml example from above including the data for ChildID
}
So my question is:
How to get ChildID of the type FosterHome, it looks like ChildID is of the type Family, so if you wanted the fosterHome object to contain the childIDs, this may not work. Do anyone know of a way to accomplish this? Something like FosterHome.Family may not be possible since the classes are decoupled. So when I do a fosterHome.getChildID() I will get a childID of type FosterHome.
I've got a nested object hierarchy which looks like this:
Profile: contains a List<Category>
Category contains a List<Script>. It is exposed via a JavaFX SimpleListProperty, so that it can be bound to via JavaFX's data binding.
Script contains nothing but simple values.
I'm just using JAXB to marshall and unmarshall POJOs. There are no databases or XML schemas involved.
Marshalling a Profile value works fine, and generates valid XML. However, unmarshalling the same XML file later, results in each Category containing an empty List<Script>. This appears to be due to the fact that Category stores the List<Script> by using a JavaFX bindable property.
Is there a way to make JAXB properly deserialize into a SimpleListProperty that contains a custom object?
Here's a minimal sample that demonstrates the same issue.
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;
}
One of the principles of StackOverflow is Minimal, Complete, and Verifiable questions. See https://stackoverflow.com/help/mcve
When creating such test code, I did this:
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; }
}
Running it produced:
<?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
It seems to work fine, on Java 8 at least.
Try it out. If it doesn't work, then you might need to upgrade Java to a newer version. If it works, you might use it as a baseline to see what might be different in your code.
I believe you're running into a weirdness of JAXB. Notice that you don't have an addCategoryScript method. So how is JAXB going to add Script objects to the list?
It could create a list of it's own, then give it to you with setCategoryScripts, but how would it know what kind of list to create?
It solves that dilemma by calling getCategoryScripts to get the initial (empty) list, and then add elements to it.
But then what happens if you returned a copy of the internal list?
Ah Ha! Call setCategoryScripts when the list is complete.
This means that it'll call setCategoryScripts with the list returned by getCategoryScripts.
I has an implementation that needed some special handling, so what I did was:
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));
}
But oops. The call to clear actually cleared the incoming objList parameter, and I ended up with nothing.
My solution was to copy the parameter list before proceeding.
I have a xml file:
<Generic_Name>
<Name>
<All>Home</All>
<link>value1</link>
</Name>
<Name>
<All> Filter</All>
<link>value2</link>
</Name>
</Generic_Name>
I need to get the value of <All> tags in a combo box.When the user select the combo box value (that is the value of <All>)its corresponding <link> value need to be printed in the console. I am getting the values of All to the combo box but I cannot get <link> values printed
DocumentBuilderFactory domFactory = DocumentBuilderFactory
.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("generic.xml");
XPath xpath = XPathFactory.newInstance().newXPath();
// XPath Query for showing all nodes value
XPathExpression expr = xpath.compile("//Name/*/text()");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
final DefaultComboBoxModel assignGp = new DefaultComboBoxModel();
// for (int i = 0; i < nodes.getLength(); i+)
int i = 0;
while (i < nodes.getLength()) {
assignGp.addElement(nodes.item(i).getNodeValue());
i = i + 2;
}
final JComboBox assignCombo = new JComboBox(assignGp);
assignCombo.setSelectedIndex(0);
assignCombo.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String selct=assignCombo.getSelectedItem().toString();
// assignGp.getSelectedItem(nodes.item(i).getNextSibling());
}
});
assignCombo.setBounds(150, 180, 140, 25);
panel.add(assignCombo);
(Same recommendation as Oliver Watkins (+1), using a JAXB approach)
You're making it difficult for yourself, doing it this way. You should just map your xml to java object. Say have Name class with all and link fields. I'm not great with dom and xpath, but if you are then just traverse the entire xml and map each set to a Name object and add all those Name objects to the DefaultListModel. Override the toString() of the Name class to just return the all field, that way, only the all field is rendered in the JComboBox
A much easier way that using dom/xpath, is to use JAXB which will do the binding for your, with annotations.
Tested Example:
Root Element class
#XmlRootElement(name = "Generic_Name")
public class GenericName {
#XmlElement(name = "Name")
protected List<Name> name;
public List<Name> getNames() {
if (name == null) {
name = new ArrayList<Name>();
}
return this.name;
}
}
Name class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Name", propOrder = {
"all",
"link"
})
public class Name {
#XmlElement(name = "All", required = true)
protected String all;
#XmlElement(required = true)
protected String link;
// getters and setters
#Override
public String toString() {
return all;
}
}
Result: Using Unmarshaller to unmarshal the xml. Passing the top level element type GenericName.class, when the xml is unmarshalled, a GenericName instance will be created, with the list of Names
import java.awt.event.*;
import java.io.File;
import java.util.List;
import javax.swing.*;
import javax.xml.bind.*;
public class TestComboJaxB {
private static final String FILE_PATH = "src/combobox/genericname.xml";
public static void main(String[] args) throws Exception {
SwingUtilities.invokeLater(new Runnable(){
public void run() {
File file = new File(FILE_PATH);
ComboBoxModel model = new DefaultComboBoxModel(getNames(file).toArray());
final JComboBox cbox = new JComboBox(model);
cbox.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
Name name = (Name)cbox.getSelectedItem();
System.out.println(name.getLink());
}
});
JOptionPane.showMessageDialog(null, cbox);
}
});
}
private static List<Name> getNames(File file) {
List<Name> names = null;
try {
JAXBContext context = JAXBContext.newInstance(GenericName.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
GenericName genericName = (GenericName)unmarshaller
.unmarshal(file);
names = genericName.getNames();
} catch (Exception ex) {
ex.printStackTrace();
}
return names;
}
}
Xml file used
<?xml version="1.0" encoding="UTF-8"?>
<Generic_Name>
<Name>
<All>Home</All>
<link>value1</link>
</Name>
<Name>
<All> Filter</All>
<link>value2</link>
</Name>
</Generic_Name>
Resources:
Oracle JAXB Tutorial
Tutorial from JAXB Specification
You should try a more object-oriented approach.
You should create an object called NameEntity, and inside it have the properties All and Link.
Iterate over your XML and create an arrayList of NameEntities.
Now iterate over the arrayList and add the elements to your combobox. You can either implement toString() to make NameEntity print the All attribute, or you can use a ListCellRenderer to explicitly render the value of NameEntitiy.
Read up here :
http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html
Now in actionPerformed(..) you will get a NameEntity instead of a String. To get the corresponding Link value, you just have to call
NameEntity ne = (NameEntity)assignCombo.getSelectedItem();
ne.getLink();
I have two classes, Package and ModelRefObj. Package contains two sets of ModelRefObj.
I'm using Simple framework to parse their instances from XML, so I've created some JUnit tests. I'm able to parse ModelRefObj XML, but I'm getting the following exception when trying to parse a Package:
org.simpleframework.xml.core.ValueRequiredException: Empty value for #org.simpleframework.xml.Text(empty=, data=false, required=true) on field 'value' private java.lang.String cz.semanta.coc.domain.cognos.ModelRefObj.value in class cz.semanta.coc.domain.cognos.ModelRefObj at line 1
at org.simpleframework.xml.core.Composite.readInstance(Composite.java:580)
at org.simpleframework.xml.core.Composite.readText(Composite.java:467)
at org.simpleframework.xml.core.Composite.access$200(Composite.java:59)
at org.simpleframework.xml.core.Composite$Builder.read(Composite.java:1381)
...
Here is the XML I'm trying to parse:
<package>
<name>GO Sales (nalysis)</name>
<visible>
<refObj>[go_sales]</refObj>
<refObj>[Filters and calculations].[Returns]</refObj>
</visible>
<hidden>
<refObj>[gosales].[BRANCH].[BRANCH_CODE]</refObj>
<refObj>[gosales].[BRANCH].[ADDRESS1]</refObj>
<refObj>[gosales].[BRANCH].[CITY]</refObj>
</hidden>
</package>
Here are my annotated classes:
#Root(name = "package")
public class Package {
#Element
private String name;
#ElementList(name = "visible", entry = "refObj", type = ModelRefObj.class)
private Set<ModelRefObj> visibleRefObjs;
#ElementList(name = "hidden", entry = "refObj", type = ModelRefObj.class)
private Set<ModelRefObj> hiddenRefObjs;
Package() { }
...
}
#Root(name = "refObj")
public class ModelRefObj {
#Text
private String value;
ModelRefObj() { }
public ModelRefObj(String value) {
this.value = value;
}
...
}
I have implemented the classes you have and used the example xml you provided.
I created a main function to test
public static void main(String args[]) throws Exception {
Serializer serializer = new Persister(new Format("<?xml version=\"1.0\" encoding= \"UTF-8\" ?>"));
File source = new File("sample.xml");
Package p = serializer.read(Package.class, source);
System.out.println(p.name);
}
The output is
GO Sales (nalysis)
Inspecting the object p in debug mode shows it has the two Sets with two and three elements.
Your code works fine for me.