How do I get JAXB to choose enum? - java

I'm trying to convert the XML text to a Java object, but there is a number in the prQueryStatus XML attribute. The type of the Java field is an enum. Is there a way for JAXB to choose my enum?
Strxml:
<custom prQueryStatus="1" ></custom>
faulty row:
CustAttrPrQuery custom = (CustAttrPrQuery)XmlOperations.deserializeFromXML(CustAttrPrQuery.class, strXmlCustom);
XmlOperations.deserializeFromXML():
public static Object deserializeFromXML(Class obj, String strXml) {
Object result = null;
JAXBContext jaxbContext;
try {
jaxbContext = JAXBContext.newInstance(obj);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(strXml);
result = unmarshaller.unmarshal(reader);
return result;
} catch (JAXBException e) {
return new String("-3 JAXB deSerialize Error");
}
}
CustAttrPrQuery:
#XmlAccessorType(javax.xml.bind.annotation.XmlAccessType.FIELD)
#XmlRootElement(name = CustAttrPrQuery.RootElement)
public class CustAttrPrQuery {
public final static String RootElement = "custom";
#javax.xml.bind.annotation.XmlAttribute
private PrQueryStatus prQueryStatus = PrQueryStatus.NONE;
public PrQueryStatus getPrQueryStatus() {
return prQueryStatus;
}
public void setPrQueryStatus(PrQueryStatus prQueryStatus) {
this.prQueryStatus = prQueryStatus;
}
}
enum:
public enum PrQueryStatus {
NONE,
ACIK,
TUMU
}

You need to annotate your enum type with #XmlEnum
and its constants with #XmlEnumValue,
so that JAXB will know how to map from XML attributes ("0", "1", "2") to the enum constants (NONE, ACIK, TUMU):
#XmlEnum
public enum PrQueryStatus {
#XmlEnumValue("0") NONE,
#XmlEnumValue("1") ACIK,
#XmlEnumValue("2") TUMU
}

Related

JAXB Parent and Child node Same name. Child node returns null values

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>

JAXB UnmarshallException unexpected element but "Expected elements are (none)"

I try make a very 'abstract' method to convert any type of Object to an XML-String and vise versa using JAXB (javax.xml.bind.*).
I get a very strange error which I don't know the meaning of.
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"Incident"). Expected elements are (none)
I have searched for numerous solutions on google and stackoverflow, yet their solution don't seem t help. I'm facing a dead end here.
My converter method
public Object convertXmlToObject(String string, Class c) throws ConversionException {
try {
JAXBContext jaxbContext = JAXBContext.newInstance(c.getClass());
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
InputStream stream = new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8));
Object converted = jaxbUnmarshaller.unmarshal(stream);
return converted;
} catch (JAXBException e) {
e.printStackTrace();
throw new ConversionException("Could not convert the message to an Object", e);
}
}
where I call the method
public void generateIncidentReport(Incident incident) throws RepositoryException, ConversionException {
ConversionTool conversionTool = new Converter();
String xmlMessage = conversionTool.convertObjectToXml(incident);
//...
}
My Incident class(which has al the needed annotations)
#XmlRootElement(name = "Incident")
#XmlAccessorType(XmlAccessType.FIELD)
public class Incident {
#XmlElement(name = "shipId")
private int shipID;
#XmlElement(name = "incidentType")
private String type;
#XmlElement(name = "action")
private String action;
#XmlElement(name = "centraleID")
private String centraleID;
#XmlElement(name = "Ship")
private Ship ship;
public Incident() {
}
//getters and setters
}
and last the XML String
<Incident><incidentType>Medisch noodgeval</incidentType><shipId>1234567</shipId></Incident>
You write
JAXBContext jaxbContext = JAXBContext.newInstance(c.getClass());
with c already being a class, therefore creating a context for java.lang.Class. What you need is
JAXBContext jaxbContext = JAXBContext.newInstance(c);

XML file to Specify Java class and return object

I have a Maven & Spring based Java web application
In src/main/resources, I have one XML file.
sourceconfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<sourceConfig area="Defects">
<adapterObject>jAdapter</adapterObject>
<resultObject>jsonObject</resultObject>
</sourceConfig>
In I have a POJO for this SourceConfig.java
#XmlRootElement
public class SourceConfig {
String area;
String adapterObject;
String resultObject;
public String getArea() {
return area;
}
#XmlAttribute
public void setArea(String area) {
this.area = area;
}
public String getAdapterObject() {
return adapterObject;
}
#XmlElement
public void setAdapterObject(String adapterObject) {
this.adapterObject = adapterObject;
}
public String getResultObject() {
return resultObject;
}
#XmlElement
public void setResultObject(String resultObject) {
this.resultObject = resultObject;
}
}
I am able to parse the xml to object.
public class SourceAdapterConfig {
public SourceConfig getConfigObject() throws JAXBException, IOException {
JAXBContext jaxbContext = JAXBContext.newInstance(SourceConfig.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Resource resource=new ClassPathResource("sourceconfig.xml");
File file=resource.getFile();
SourceConfig sourceConfig = (SourceConfig) jaxbUnmarshaller.unmarshal(file);
return sourceConfig;
}
}
It is working fine.
But all are String. Some I want as object. For example, In XML I have mentioned <resultObject>jsonObject</resultObject>
I have a class com.myapp.config.JsonObject.java
So, instead of <resultObject>jsonObject</resultObject> If I mention class like this
<resultObject class="com.myapp.config.JsonObject">jsonObject</resultObject>
or some other way to mention class, I should be able to get a JsonObject object in my SourceConfig How can I do that?
use java reflection
Class theClass = Class.forName("com.example.Test");
Test testObject = (Test)theClass.newInstance();
This will create an instance of com.example.Test.
In your context,
public class SourceAdapterConfig {
private SourceConfig config;
private SourceConfig getConfigObject() throws JAXBException, IOException {
JAXBContext jaxbContext = JAXBContext.newInstance(SourceConfig.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Resource resource=new ClassPathResource("sourceconfig.xml");
File file=resource.getFile();
SourceConfig sourceConfig = (SourceConfig) jaxbUnmarshaller.unmarshal(file);
return sourceConfig;
}
public SourceAdapterConfig(){
config = getConfigObject();
}
public Object getAdapterObject(){
String adapterClassName = config.getAdapterObject();
Class theClass = Class.forName(adapterClassName);
return theClass.newInstance();
}
}
Usage:
SourceAdapterConfig config = new SourceAdapterConfig();
Object adapterObject = config.getAdapterObject();

Sourceforge SimpleXML Enum serialization

SimpleXML can serialize a Java Enum fine but when it comes to de-serialization, it returns null instead of creating Enum from the generated XML. Is it something I am doing wrong of Enum serialization is not supported at all?
Serialization returns this:
<TestStatus>
<status>Functional</status>
</TestStatus>
Test Enum:
#Root
public enum TestStatus {
AVAILABLE("Functional"),
NOT_AVAILABLE("Dysfunctional");
#Element
private String status;
private Status(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
}
How do you serialize your enum?
if you use it like this, it should work without problems but will return some different XML:
Example:
#Root
public class Example
{
#Element
private TestStatus status = TestStatus.AVAILABLE;
// ...
}
Test:
final File f = new File("test.xml");
Serializer ser = new Persister();
ser.write(new Example(), f);
Example m = ser.read(Example.class, f);
XML:
<example>
<status>AVAILABLE</status>
</example>
You can rename the xml-tags with annotationarguments, but the value wont be changeable.
Another (possible) solution is using a custom converter:
Annotations of the enum:
#Root()
#Convert(TestStatusConverter.class)
public enum TestStatus
{
// ...
}
Converter (Example)
public class TestStatusConverter implements Converter<TestStatus>
{
#Override
public TestStatus read(InputNode node) throws Exception
{
final String value = node.getNext("status").getValue();
// Decide what enum it is by its value
for( TestStatus ts : TestStatus.values() )
{
if( ts.getStatus().equalsIgnoreCase(value) )
return ts;
}
throw new IllegalArgumentException("No enum available for " + value);
}
#Override
public void write(OutputNode node, TestStatus value) throws Exception
{
// You can customize your xml here (example structure like your xml)
OutputNode child = node.getChild("status");
child.setValue(value.getStatus());
}
}
Test (enum):
final File f = new File("test.xml");
// Note the new Strategy
Serializer ser = new Persister(new AnnotationStrategy());
ser.write(TestStatus.AVAILABLE, f);
TestStatus ts = ser.read(TestStatus.class, f);
System.out.println(ts);
Test (class with enum):
As above but with AnnotationStrategy
You don't need to add annotations to enums, they serialize automatically.

Why doesn't JAXB handle namespaced child elements properly?

I'm using JAXB with a CXF Web Service. When I pass:
<?xml version="1.0" ?><ns2:Optionen xmlns:ns2="http://test.at/ezustellung/global/20090501#">
<ns2:PdfAKonvertierung>true</ns2:PdfAKonvertierung><ns2:SignaturTyp>Adobe</ns2:SignaturTyp>
</ns2:Optionen>
to the Unmarshaller, the properties pdfAKonvertierung und signaturTyp are both null in my Object. But if I pass:
<?xml version="1.0" ?><ns2:Optionen xmlns:ns2="http://test.at/ezustellung/global/20090501#">
<PdfAKonvertierung>true</PdfAKonvertierung><SignaturTyp>Adobe</SignaturTyp>
</ns2:Optionen>
which is invalid, according to the CXF Validator and wikipedia (translation, haven't found that in english):
Child elements of an element with a namespace prefix do not automatically have the same namespace, they have to be prefixed with a namespace as well.
The properties are set accordingly. Can someone spot an error in my code, or have I found a bug in the JAXB implementation from Java 1.6?
For reference, here is my code:
public class JaxbTests {
#Test
public void testOptionen() throws JAXBException, SAXException, IOException {
JAXBContext context = JAXBContext.newInstance(Optionen.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
ByteArrayInputStream is = new ByteArrayInputStream(
// Does not work
("<?xml version=\"1.0\" ?><ns2:Optionen xmlns:ns2=\"http://test.at/ezustellung/global/20090501#\">" +
"<ns2:PdfAKonvertierung>true</ns2:PdfAKonvertierung><ns2:SignaturTyp>Adobe</ns2:SignaturTyp>" +
"</ns2:Optionen>").getBytes());
// Works
// ("<?xml version=\"1.0\" ?><ns2:Optionen xmlns:ns2=\"http://test.at/ezustellung/global/20090501#\">" +
// "<PdfAKonvertierung>true</PdfAKonvertierung><SignaturTyp>Adobe</SignaturTyp>" +
// "</ns2:Optionen>").getBytes());
Optionen opts = ((Optionen) unmarshaller.unmarshal(is));
Assert.assertTrue(opts.isPdfAKonvertierung() == true);
Assert.assertEquals(SignaturTypType.ADOBE, opts.getSignaturTyp());
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"pdfAKonvertierung",
"signaturTyp"
})
#XmlRootElement(name = "Optionen")
public class Optionen {
#XmlElement(name = "PdfAKonvertierung", defaultValue = "true")
protected Boolean pdfAKonvertierung;
#XmlElement(name = "SignaturTyp", defaultValue = "Adobe")
protected SignaturTypType signaturTyp;
public Optionen() {
System.out.println("Optionen created");
}
public Boolean isPdfAKonvertierung() {
return pdfAKonvertierung;
}
public void setPdfAKonvertierung(Boolean value) {
this.pdfAKonvertierung = value;
}
public SignaturTypType getSignaturTyp() {
return signaturTyp;
}
public void setSignaturTyp(SignaturTypType value) {
this.signaturTyp = value;
}
}
#XmlType(name = "SignaturTypType")
#XmlEnum
public enum SignaturTypType {
#XmlEnumValue("Adobe")
ADOBE("Adobe"), #XmlEnumValue("PDF-AS")
PDF_AS("PDF-AS");
private final String value;
SignaturTypType(String v) {
this.value = v;
}
public String value() {
return this.value;
}
public static SignaturTypType fromValue(String v) {
for (SignaturTypType c : SignaturTypType.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
package-info.java:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://test.at/ezustellung/global/20090501#")
package at.test.ezustellung.global._20090501_;
The solution (without modifying the generated classes) to the problem was to make sure that the
<xs:schema elementFormDefault="qualified">
attribute is present and to regenerate the jaxb mapping, so
that
elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED
ends up in the package-info.java
It's a little bit difficult to read your incorrectly formatted code, but wouldn't it fix your problem if you declare your elements with the correct namespace, e.g. #XmlElement(namespace = "http://test.at/ezustellung/global/20090501#", name = "SignaturTyp", defaultValue = "Adobe")?

Categories

Resources