I have different XML returns that I want to parse with JAXB with annotated classes. Many of the XML files share a similar top structure and with the contents of an inner tag that can vary. Since we sent the ContextInstance a class and not an instance I can't add classes.
How would you create a set of JAXB annotations that don't repeat the top level xml each time, given examples of two files like this.
<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
<currentTime>2008-11-24 20:14:29</currentTime>
<result>
<serverOpen>True</serverOpen>
<onlinePlayers>38102</onlinePlayers>
</result>
<cachedUntil>2008-11-24 20:17:29</cachedUntil>
</eveapi>
And another file like this:
<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
<currentTime>2012-02-12 14:39:12</currentTime>
<result>
<rowset name="characters" key="characterID" columns="name,characterID,corporationName,corporationID">
<row name="Alexis Prey" characterID="1365215823" corporationName="Puppies To the Rescue" corporationID="238510404"/>
</rowset>
</result>
<cachedUntil>2012-02-12 15:27:00</cachedUntil>
</eveapi>
A common top level class would be something like
#XmlRootElement(name="eveapi")
public class EveApiConfig {
#XmlElement(required = true)
protected Date currentTime;
#XmlElement(required = true)
protected String cachedUntil;
#XmlAttribute(name = "version", required = true)
protected int version;
#XmlElement(required = true)
protected ResultsConfig result;
}
But ResultsConfig would be number of other things. it's almost like a need a reverse XmlSeeAlso
Any thoughts if how to do this?
I think you can achieve this with several object factories. I'll sketch out the solution:
Make result an #XmlElementRef, typed with something like JAXBElement<AbstractResultConfig>
Per case create one package with an ObjectFactory
In which of the ObjectFactory classes implement a different #XmlElementDecl factory method which would create JAXBElements with instance of different subclasses of the AbstractResultConfig. Something like:
#XmlElementDecl(name = "Result")
public JAXBElement<ResultConfigImpl> createResultConfigImpl(ResultConfigImpl value) {
return new JAXBElement<ResultConfigImpl>(..., ResultConfigImpl.class, null, value);
}
I guess you'll need to pair ith with createResultConfig() method or something like this.
Related
I am trying to create XML using the Moxy Marshalling approach. Everything seems to be working fine except for one small thing. Basically, I would like to know how to add wrapper element to the class itself during the marshalling.
As we are aware we can add #XmlPath("test/name/text()") to add a wrapper to any of the String elements. Also for collection we can make use of #XmlElementWrapper(name="languages"), #XmlElement(name="language"). But these are for the fields within the class. How can I add the wrapper element to the class itself which is being marshalled?
I have the following class:
#XmlRootElement(name = "customer")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer{
#XmlPath("extension/name/text()")
private String name;
#XmlElementWrapper(name = "languages")
#XmlElement(name = "language")
private List<String> languages;
//Getter, Setter and other constuctors
}
This will produce the output XML something like this:
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
However, I would like to add the wrapper element to event the whole class so that output would look something like this: (Notice the whole customer is wrapper within classWrapper)
<classWrapper>
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
</classWrapper>
I tried adding the #XmlPath and #XmlElementWrapper to the Customer class but it throws an error as it can be added only to the fields within the class but not to the whole class.
Following is my Main class which would be used for marshalling:
public class HelloWorld{
public static void main(String []args){
Customer customer = new Customer();
List<String> languages = new ArrayList<String>();
languages.add("English");
languages.add("UK English");
customer.setName("Hello");
customer.setLanguages(languages);
JAXBContext context = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer,System.out);
}
}
I am aware that I can write another wrapper class add the custom field to it then use that wrapper class for the marshalling. Actually, I am doing the same as of now. However, trying to find if there is some Moxy approach of doing this similar to how we have #XmlPath and #XmlElementWrapper
Can someone please suggest some form of Moxy Jaxb related approach for achieving the wrapper element to the whole class?
This is a temporary workaround that works as expected for me. Posting the answer here so it can be helpful to someone in the future.
Change the #XmlRootElement("customer") to #XmlRootElement("classWrapper"). So you will get the classWrapper as the outer element.
Then change all the element within the Customer class using the #XmlPath so that all element go under the Customer tag. So overall customer.class would look something like this:
#XmlRootElement(name = "classWrapper")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
public class Customer{
#XmlPath("customer/extension/name/text()")
private String name;
#XmlPath("customer/languages/language/text()")
private List<String> languages;
//Getter, Setter and other constuctors
}
Just a tip while using the #XmlPath:
do not use text() if its not simple type such as String,Date, etc type.
For example if the element is of Custom type with List then do not use /text()
#XmlPath("extension/elements/element")
List<myType> elements;
This will add extension -> elements -> element then content.
<extension>
<elements>
<element></element>
<element></element>
......
</elements>
</extension>
If the elements are of String type then you have to use text()
#XmlPath("extension/elements/text()")
List<String> elements;
I'm able to unmurshal a single occurrence of a dynamic xml instantiating java classes (a wrapper and an adapter), but I don't understand how to extend this mechanism to a list of occurrences; the xml is like
<ALLRECORDSDATASET>
<RECORD>
<FIELD_0001>000248031</FIELD_0001>
<FIELD_0022>A</FIELD_0022>
<FIELD_0031>0</FIELD_0031>
<FIELD_0033>1994-01-01</FIELD_0033>
</RECORD>
<RECORD>
<FIELD_0001>000248056</FIELD_0001>
<FIELD_0027>ABC</FIELD_0027>
<FIELD_0037>DEF</FIELD_0037>
<FIELD_0040>1994-01-01</FIELD_0040>
</RECORD>
</ALLRECORDSDATASET>
and I can get the last values of RECORD (having a Record class containing a Fields class using #XmlAnyElement annotation), but I can't get all the RECORD list.
Can anyone help me? Thanks
Thats one way of doing that
#XmlRootElement(name = "ALLRECORDSDATASET")
#XmlAccessorType(XmlAccessType.NONE)
public class DataSet {
#XmlElements({ #XmlElement(name = "RECORD", type = Record.class) })
private List<Record> records;
}
#XmlAccessorType(XmlAccessType.NONE)
public class Record {
#XmlElement(name = "FIELD_0001")
private String field;
// ....
}
I'm parsing an XML document which has a fairly complex data structure.
Example:
<Companies>
<LISTID>6353HHJDLS628JNHJ6</LISTID>
<Company>
<ID>123ABC</ID>
<Value>True</Value>
<Order>
<Text>Because </Text>
<ListOfReasons>
<InputName>
<Text>your company did not meet requirements</Text>
</InputName>
<Text>, </Text>
<InputName>
<Text>your company was not listed as qualified</Text>
</InputName>
<Text> etc...</Text>
</ListOfReasons>
</Order>
</Company>
<Company>
<ID>123DEF</ID>
<Value>False</Value>
<Order>
<Text>We can't get any more details on </Text>
<NodeName>
<Text>neither your company or the entity in question</Text>
</NodeName>
<Text> right now.</Text>
</Order>
</Company>
</Companies>
</root>
How should I model my pojo class? To me it seems it should have nested or inner classes. I am not sure how this would look like
I know all about JaxB but I don't really know how to use it and unless there is some easy way to implement it, I prefer writing a pojo, because I understand it.
I'm DOM parsing and I'd like to represent it in Java objects. That is the purpose for writing this model. Can anyone give me an example data model class using the XML I've shown. Any help or assistance would be much appreciated.
There's a great tool that will get you started here.
In your case there are classes like this:
#XmlRootElement
class Companies {
private String LISTID;
private List<Company> companies;
#XmlElement(name = "company")
public void setCompany(List<Company> companies) {
this.companies = companies;
}
#XmlElement
public void setLISTID(String LISTID) {
this.LISTID = LISTID;
}
/** Others standard POJO Methods */
}
#XmlRootElement
public class Company {
private String id;
private String value;
private List<Order> orders;
/** Like in the previous example**
}
With the tool showed above, your work will only to decorate classes with #Xml* annotations.
I have surely a problem of understanding of Xstream converter.
I have a pretty complex XML coming from legacy back office application and a just want to convert this one into a simpler java class or a Map with Converter.
I do not want to marshal to XML, just unmarshal
I do not want to use annotation, just KISS
My XML is like
<root>
<agent>
<id>123456789</id>
<name>GABIN</name>
<forname>Jean</forname>
<unit>
<legacyReference>AA</legacyReference>
<name>SAN ANTONIO</name>
<legacyName>SA</legacyName>
<address>
<number>1</number>
<street>Sesam street</street>
<city>NoWhere</city>
<legacyID>AAZE-56</legacyID>
</address>
<legacyStructurBlablabla>
<type>OFFICE</type>
<data>
<foo>BAR</foo>
</data>
</legacyStructurBlablabla>
<...>
</unit>
<...>
</agent>
</root>
My destination class is very simple
class Agent {
String id;
String name;
String forname;
String unitName;
String unitType;
<...>
}
My main method looks like
xStream = new XStream(new DomDriver());
stream = this.getClass().getClassLoader().getResourceAsStream("data/agent.xml") ;
xStream.alias("root", Map.class);
xStream.alias("agent", Agent.class);
xStream.registerConverter(new ResultConverter());
xStream.registerConverter(new AgentConverter());
xStream.ignoreUnknownElements();
Object obj = xStream.fromXML(stream);
I don't understand how to step down in favor of another converter.
public class ResultConverter implements Converter {
...
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Map<String, Object> agents = new HashMap<String, Object>();
while( reader.hasMoreChildren() ) {
reader.moveDown();
// HOW I DO TO STEP DOWN IN FAVOR OF Agent CONVERTER ?
reader.moveUp();
}
return agents;
}
I didn't see Xstream scrolled dow the thierarchy to activate my AgentConverter.
I surely missing the point
EDIT
Xstream may be not thr right tool for this.
I would use xmlbeam, according to Cfx advise.
XMLBeam projects the XML to a Java Class according to XPATH mapping.
Seems that you want the xml structure not to be reflected in your Java class. If you stick to XStream, I have no solution. But there is an alternative framework that was made to solve exactly this issue. It uses annotations, but IMHO does not violate the KISS principle.
Here is an example:
public interface Agent {
#XBRead("/root/agent/id")
String getId();
#XBRead("/root/agent/name")
String getName();
#XBRead("/root/agent/forename")
String getForname();
#XBRead("/root/agent/unit/name")
String getUnitName();
#XBRead("/root/agent/unit/legacyStructurBlablabla/type")
String getUnitType();
}
You will define interfaces instead of classes, but the instances of these interfaces are useable just like POJOs. (with toString(), equals(), hashCode,...).
Creating instances of these interfaces is like this:
Agent agent = new XBProjector().io().stream(stream).read(Agent.class);
I need to map some legacy XML I can't change. There are several elements that have hundreds attributes exactly the same as some other elements. The attributes all have the same name postfixed with a number. So XML might look like this:
<someElement custom1="..." custom2="..." custom78=".."/>
<anotherElmenent custom1="..." custom45="..."/>
A solution that "works" is to create a base class like so:
#XmlAccessorType(FIELD)
public class LotsaCustomIds
{
#XmlAttribute
private String custom1;
#XmlAttribute
private String custom2;
...
}
#XmlType
public class SomeElement extends LotsaCustomIds
{
....
}
But it's a shame to use inheritence here, especially since Java only has single inheritence. What I'd like to do is something like the way JPA/Hibernate do embedded objects, like:
#XmlType
public class SomeElement
{
#EmbeddedAttributes
private LotsaCustomIds customIds;
....
}
Anyway to do this?
Note: I'm the EclipseLink JAXB (MOXy) lead.
You could use MOXy's #XmlPath extension to map this use case. When you use it as #XmlPath(".") then it will pull the contents of the child object (LotsaCustomIds) into the parent object (SomeElement).
#XmlType
public class SomeElement
{
#XmlPath(".")
private LotsaCustomIds customIds;
....
}
Related Information from my Blog
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html