JAXB - XmlElement with multiple types - java

I'm working with jaxb and with xml I'm doing jpa. I had such a question. I keep a list of records in the Catalog. Previously, it was a list from a particular entity, but now I want to be able to do any entity. Tell me how can I do this?
I want to specify in the root element a class with an entity and somehow apply this to the list
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "catalog")
public class Catalog {
#XmlAttribute
private String id;
#XmlAttribute(name = "class")
private String className;
#XmlElement(name = "record")
private List<Book> recordList = new ArrayList<Book>();
<--Getters and Setters-->
This my XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalog id="1" type="extendable" class="cataloghandler.entities.Books">
<record>
<title>book1</title>
<description>hello12345</description>
</record>
<record>
<title>book2</title>
<description>goodbye</description>
</record>
</catalog>
And I want to add another Entity, for example, TVShow, with another fields.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalog id="6" type="extendable" class="cataloghandler.entities.TVShow">
<record>
<channel>fox</channel>
<time>10:45</time>
</record>
<record>
<channel>abc</channel>
<time>12:00</time>
</record>
</catalog>
How I can change code so, that List have type List<TVShow>.

Related

Multiple namespaces with JAXB

I am working on a XML file with multiple naespaces and I am trying ot unmarshal it.
I have looked into some questions on stack overflow previously but have not yet met with the solution.
The XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
The Registry, Student class
#Data
#XmlRootElement(name="Registry")
#XmlAccessorType(XmlAccessType.FIELD)
public class Registry {
#XmlElement
private List<Student> Student;
}
Student:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
//#XmlType(namespace = "http://www.registar.com/Grad")
public class Student {
#XmlElement
private String FirstName;
private Value value; // suggestions to achieve this with different namespaces
}
I have a package-info.java file
#XmlSchema(
namespace="http://www.registar.com",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms",namespaceURI = "http://www.registar.com/Grad")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
import javax.xml.bind.annotation.*;
And i try to print out the unmarshalled entries. However the different namespaces are not recognised.
The output shows an error..
unexpected element (URI: "http://www.registar.com", local: "Registry"). Expected elements are <{} Registry>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:744)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:262)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:257)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:124)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1149)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:574)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:556)
at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:168)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:518)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.scanStartElement(XMLNSDocumentScannerImpl.java:374)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl$NSContentDriver.scanRootElementHook(XMLNSDocumentScannerImpl.java:613)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:3078)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$PrologDriver.next(XMLDocumentScannerImpl.java:836)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:605)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:112)
at java.xml/com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:541)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:888)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:824)
at java.xml/com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at java.xml/com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1224)
at java.xml/com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:635)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:229)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:170)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:209)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:194)
at Mainparser.main(Mainparser.java:20)
Could you please suggest or provide some guidance here?
Update: As per the suggestion, I passed the prefix of ns0 to the default namespace and made the changes. I do not get any error, but the output is not as expected
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Registry>
<Student>
<FirstName>RP</FirstName>
<Value/>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value/>
</Student>
</Registry>
UPDATE2:
The xmlns:ms has the different scorevariants( it could be string or int or double).. I am not able to extract this information as well.. There needs to be a Value class, but I do not seem to find a way to extract the information
Update 3:
The value class was written as per the suggestion.
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElements( value = {
#XmlElement(name="ms:String",type = String.class),
#XmlElement(name="ms:Int",type = Integer.class)
})
private Object value;
}
The output is as below:
<Registry xmlns:ms="http://www.registar.com/ScoreVariant" xmlns="http://www.registar.com">
<Student>
<FirstName>RP</FirstName>
<Value/>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value/>
</Student>
</Registry>
Rest of the code is the same.. Please suggest if there is any dependency that I am missing on..
You would be able to achieve this by doing the following:
Your package-info.java
#XmlSchema(
namespace = "http://www.registar.com",
elementFormDefault = XmlNsForm.QUALIFIED,
xmlns = {
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "", namespaceURI = "http://www.registar.com/Grad"),
#XmlNs(prefix = "ms", namespaceURI = "http://www.registar.com/Value")
}
)
#XmlAccessorType(XmlAccessType.FIELD)
Your Value class, which you did not provide here, so I made an assumption of how it would look like:
#XmlRootElement(name = "Value", namespace = "http://www.registar.com/Value")
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElement(name = "String", namespace = "http://www.registar.com/Value")
private String string;
#XmlElement(name = "Int", namespace = "http://www.registar.com/Value")
private Integer number;
}
Once you do the Marshalling your XML will look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/Value"
xmlns:ns3="http://www.registar.com/Grad">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
Here the namespace is registered and configured to have a prefix, subsequently we tell the fields in the Value class which namespace they're using.
My experience of the actual specs of SOAP are not sufficient to tell you whether this would be a valid SOAP. My testing however allowed for both marshalling and unmarshalling if this XML file.
I believe this is something you are looking for:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>
Registry:
#XmlRootElement(name = "Registry")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Registry {
#XmlElement(name = "Student")
List<Student> Student;
}
Student:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
#XmlElement(name="FirstName")
private String FirstName;
private Value Value;
}
Value:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Value {
#XmlElements({
#XmlElement(name = "ms:String", type = String.class),
#XmlElement(name = "ms:Int", type = Integer.class)
})
private Object value;
}
package-info.java:
#XmlSchema(
elementFormDefault = XmlNsForm.QUALIFIED,
namespace = "http://www.registar.com",
xmlns = {#XmlNs(prefix = "", namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms", namespaceURI = "http://www.registar.com/ScoreVariant")})
package stackover;
import jakarta.xml.bind.annotation.XmlNs;
import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
Main:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Registry.class).createUnmarshaller();
final Registry registry = unmarshaller.unmarshal(xmlStreamReader, Registry.class).getValue();
System.out.println(registry.toString());
Marshaller marshaller = JAXBContext.newInstance(Registry.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(registry, System.out);
}
}
package-info.java
#XmlSchema(
namespace="http://www.registar.com",
xmlns={
#XmlNs(prefix = "",namespaceURI = "http://www.registar.com"),
#XmlNs(prefix = "ms",namespaceURI = "http://www.registar.com/ScoreVariant")
},
elementFormDefault=XmlNsForm.QUALIFIED
)package code;
//#XmlAccessorType(XmlAccessType.FIELD)
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Registry(Student=[Student(FirstName=RP, Value=Value(value=Pass)), Student(FirstName=SK, Value=Value(value=100))])
<Registry xmlns="http://www.registar.com" xmlns:ms="http://www.registar.com/ScoreVariant">
<Student>
<FirstName>RP</FirstName>
<Value>
<ms:String>Pass</ms:String>
</Value>
</Student>
<Student>
<FirstName>SK</FirstName>
<Value>
<ms:Int>100</ms:Int>
</Value>
</Student>
</Registry>

Deserialization of xml with attribute AND value

i have little bit trouble with deserialization of XML. I am only able to deserialize this xml by:
#JacksonXmlProperty(localName = "field")
#JacksonXmlElementWrapper(useWrapping = false)
List<Object> field;
This is my xml:
<Response>
<user>
<field attribute="x"></field>
<field attribute="y">false</field>
<field attribute="z">string</field>
</user>
<user>
<field attribute="x"></field>
<field attribute="y">false</field>
<field attribute="z">string</field>
</user>
</Response>
Problem is, that i want to replace Object in List<Object> field; with some specific class so i could access attribute and the value in field.
With Object, i am able to create something like this:
user='[{attribute=x}, {attribute=y, =false}, {name=z, =string}]
thanks a lot.
Found answer. I created new class that contains this elements:
#JacksonXmlProperty(isAttribute = true, localName = "attribute")
String attribute;
#JacksonXmlText
String value;
And replace the Object with this new class.

unmarshal using jaxb having inner tag

I want to unmarshal my below xml file using JAXB.
<School>
<Student>
<Name> My xyz<Name>
<Hobbies> Playing Cricket <sup>+</sup> Watching TV</Hobbies>
</Student>
</School>
In Above example , There is one school with student which having hobbies.
Whenever i unmarshal above xml using (#XmlPath) i got only "Watching TV" but not "Playing Cricket".
I have tried to unmarshal using "DomHandler" also but not success.
Thanks.
I think your XML should be something like this:
<?xml version="1.0" encoding="UTF-8"?>
<School>
<Student>
<Name>My xyz</Name>
<Hobbies>
<Name>Playing Cricket</Name>
<Hobby>
</Hobby>
<Name>Playing Cricket</Name>
</Hobbies>
</Student>
<!-- OTHER STUDENTS FOLLOW-->
</School>
and then the correspondant java object structure would be something like:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "School")
class School {
#XmlElement(required = true, name = "Student")
Student student;
}
#XmlAccessorType(XmlAccessType.FIELD)
class Hobby {
#XmlElement(required = true, name = "Name")
String name;
}
#XmlAccessorType(XmlAccessType.FIELD)
class Student {
#XmlElement(required = true, name = "Name")
String name;
#XmlElement(required = true, name = "Hobbies")
List<Hobby> hobbies;
}
I took off the getters and the setters for brevity.

JAXB Generate xmlns:xsi declaration in a nested node

I am looking to control where JAXB generates the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" declaration when marshalling to XML. I've seen solutions like this to add it to the root element using the JAXB_SCHEMA_LOCATION property, however I don't want it on the root node, I want it somewhere in between. Here's what I've got:
#XmlRootElement(name = "RootNode")
#XmlAccessorType(XmlAccessType.NONE)
public class RootNode {
#XmlElement(name = "IntermediateNode")
private IntermediateNode intermediateNode;
//getter & setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class IntermediateNode {
#XmlElement(name = "MyEntity")
private MyEntity myEntity;
//getter and setter
}
#XmlAccessorType(XmlAccessType.NONE)
public class MyEntity {
#XmlElement(name = "Name")
private String name;
#XmlElement(name = "Title", nillable = true)
private String title;
//getters and setters
}
Serialize like:
MyEntity myEntity = new MyEntity();
myEntity.setName("George");
myEntity.setTitle(null);
IntermediateNode intNode = new IntermediateNode();
intNode.setMyEntity(myEntity);
RootNode rootNode = new RootNode();
rootNode.setIntermediateNode(intNode);
JAXBContext context = JAXBContext.newInstance(RootNode.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(rootNode, System.out);
Produces XML like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode>
<MyEntity>
<Name>George</Name>
<Title xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
But what I want is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode>
<IntermediateNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MyEntity>
<Name>George</Name>
<Title xsi:nil="true"/>
</MyEntity>
</IntermediateNode>
</RootNode>
I even tried moving my IntermediateNode and MyEntity classes into their own package with a package-info.java like this, but that just rolled the xmlns:xsi up to the root element.
#javax.xml.bind.annotation.XmlSchema(
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
},
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
package com.example.intermediate;
Is it possible to get what I want?

jaxb reference xmlID between xml files

I tried the approach in this post
However I am getting a
>
1 counts of IllegalAnnotationExceptions
XmlIDREF property is referencing a type "java.lang.String" that doesn't have an XmlID property.
this problem is related to the following location:
at private externalReferences.Department
externalReferences.Employee.department
at externalReferences.Employee
at private java.util.List externalReferences.Company.employees
at externalReferences.Company
The two xml Files are the following:
employee.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
<employeeList>
<employee name="Jane Doe" id="1">
<department>1</department>
</employee>
<employee name="John Smith" id="2">
<department>2</department>
</employee>
<employee name="Anne Jones" id="3">
<department>3</department>
</employee>
</employeeList>
</company>
department.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<departmentList>
<departmentList>
<department name="Dev" id="1"/>
<department name="Sales" id="2"/>
<department name="Research" id="3"/>
</departmentList>
</departmentList>
The employee.xml references the department and I want to point to the right department objects when unmarshalling employee.xml.
Classes are as follows:
Company.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Company {
#XmlElementWrapper(name = "employeeList")
#XmlElement(name="employee")
private List<Employee> employees;
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
public Company() {
employees = new ArrayList<Employee>();
departments = new ArrayList<Department>();
}
...
}
Employee.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlAttribute
#XmlID
private String id;
public String getId() {
return id;
}
#XmlIDREF
private Employee manager;
#XmlJavaTypeAdapter(EmpAdapter.class)
#XmlIDREF
private Department department;
}
Department.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Department {
#XmlAttribute
#XmlID
private String id;
...
}
DepartmentList.java
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class DepartmentList {
#XmlElementWrapper(name = "departmentList")
#XmlElement(name="department")
private List<Department> departments;
Then I run the following in Main
JAXBContext jc = JAXBContext.newInstance(DepartmentList.class); Unmarshaller unmarshaller = jc.createUnmarshaller();
DepartmentList depList = (DepartmentList) unmarshaller.unmarshal(new FileReader(DepRef));
EmpAdapter adapter = new EmpAdapter();
for(Department dep : depList.getDepartments()) {
adapter.getDepList().put(dep.getId(), dep);
}
JAXBContext jc2 = JAXBContext.newInstance(Company.class);
Unmarshaller unmarshaller2 = jc2.createUnmarshaller();
unmarshaller2.setAdapter(adapter);
Company company2 = (Company) unmarshaller2.unmarshal(new FileReader(empRef));
I feel that having one XMLIDREF refer to employee id and the other XMLIDREF refer to department id is part of the problem. But that is required since the manager field references other employee objects.
Can someone please help me with this. Thank you
The problem arises from class Company that corresponds to an XML document containing employees and departments. However, you've got two separate documents. Apparently you want one final class containing both lists.
(1) You could define a class EmployeeList for employees only, similar to the one for departments (DepartmentList). This will still let you write an application class Company into which you set the references for both lists.
(2) Change the annotation for Company.departments
#XmlTransient
private List<Department> departments;
marshal like you do now, and set the List with the reference you have from unmarshalling the corresponding XML into the returned object.

Categories

Resources