XML response incorrect Spring REST - jaxb

I have a problem when generating XML response of my bean
Controller's method :
#GetMapping(value = PING_URL, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<Pong> getPingPage() {
ServicesType services = new ServicesType();
services.getService().add(this.commonService.getDBSservice());
final Pong pong = new Pong();
pong.setVersion(this.commonService.getVersion());
pong.setServices(services);
pong.setStatus(this.commonService.getGlobalStatus(pong).name());
return ResponseEntity.ok(pong);
}
Bean :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"status",
"version",
"services"
})
#XmlRootElement(name = "pong")
public class Pong {
#XmlElement(required = true)
protected String status;
#XmlElement(required = true)
protected String version;
#XmlElement(required = true)
protected ServicesType services;
// GETTER / SETTER
Response :
<Pong>
<status>OK</status>
<version/>
<services>
<service>
<service>
<status>OK</status>
<log/>
<name>BDD</name>
</service>
</service>
</services>
</Pong>
As you can see, xmlRootElement is "Pong" instead of "pong" but it was ignored.
Have you any idea how to resolve the issue please ?
I'm using Spring (5.3.14) with Jackson (2.13.1).

I was able to solve it by registering an XML Configuration that tells Jackson to use a Mapper that has the JaxbAnnotationModule registered.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
#Configuration
public class MySpringRestServiceXmlConfiguration {
/**
* The converter must be declared for #XMLRootElement and #XmlElement annotations to work properly.
*/
#Bean
MappingJackson2XmlHttpMessageConverter getMappingJackson2XmlHttpMessageConverter() {
var jaxbAnnotationModule = new JaxbAnnotationModule();
var mappingJackson2XmlHttpMessageConverter = new MappingJackson2XmlHttpMessageConverter();
mappingJackson2XmlHttpMessageConverter.getObjectMapper().registerModule(jaxbAnnotationModule);
return mappingJackson2XmlHttpMessageConverter;
}
}
Don't forget to put this in a package that is scanned by your Spring Rest Service Application.

Related

Ignore xml element while marshalling and include xml element while unmarsalling by using XmlTransient annotation

I am trying to unmarshal an XML into an object that I expect should have a certain field. However, I do not want to marshal that object into an XML that contains it. What I like would be similar to this:
#XmlRootElement(name = "User")
public class User {
#XmlElement(namespace = "http://www.........", required = false)
private String name;
}
While marshalling , am getting below xml,
Current xml output file looks like:
<User xmlns:ns5="http://www......." />
But I don't want this namespace in output file,
Expected looks like below xml output file,
<User />
I tried to use #XmlTransient annotation,
#XmlRootElement(name = "User")
public class User {
#XmlTransient
private String name;
}
Worked successfully while marshalling and got exception while unmarshalling
org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: No descriptor found while unmarshalling element mapped to user name
Any help would be greatly appreciated.
As per my understanding, you would not want to get the namespaces in your XML. Based on my understanding providing the answer here. Hope it helps or at least guides you in the right direction.
I have done something like this:
import jakarta.xml.bind.Marshaller;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlTransient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#AllArgsConstructor
#XmlRootElement(name = "User")
public class User {
#XmlTransient
#XmlElement(namespace = "http://www.google/test.com", required = false)
private String name;
private String name1;
public void beforeMarshal(Marshaller m) {
//Executed after reading the data but before marshalling to XML. Here you can perform whatever you would like.
name1 = name;
name = null;
}
public void afterUnmarshal(Unmarshaller m, Object parent) {
//Executed after Unmarshalling the data. Here you can perform whatever you would like.
}
}
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Marshaller;
import javax.xml.stream.XMLStreamException;
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
User user = new User();
user.setName("Batman");
final JAXBContext jaxbContext = JAXBContext.newInstance(User.class);
final Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(user, System.out);
}
}
This would result in following XML:
<?xml version="1.0" encoding="UTF-8"?>
<User>
<name1>Batman</name1>
</User>
You can perform accordingly whatever you need within the beforeMarshal and afterMarshal if required.

How to set the namespace of marshalled xml using camel jaxb?

For starters, I'm creating some routes using Camel ver 2.15 (in Fuse 6.2.1).
In my route, i'm trying to create a XML from a pojo that was generated using cxf-xjc maven plugin (cxf-xjc read some xsd somewhere then from the xsd, the pojos with jaxb annotations were produced).
The pojos are TempProject and TempProjects.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"ecode", "tempName"}
)
#XmlRootElement(
name = "TempProject"
)
public class TempProject implements Serializable {
#XmlElement(
name = "Ecode",
required = true
)
protected String ecode;
#XmlElement(
name = "TempName",
required = true
)
protected String tempName;
public TempProject() {
}
public String getEcode() {
return this.ecode;
}
public void setEcode(String value) {
this.ecode = value;
}
public String getTempName() {
return this.tempName;
}
public void setTempName(String value) {
this.tempName = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(
name = "",
propOrder = {"tempProjects"}
)
#XmlRootElement(
name = "TempProjects"
)
public class TempProjects implements Serializable {
#XmlElement(
name = "TempProject",
required = true
)
protected List<TempProject> tempProjects;
public TempProjects() {
}
public List<TempProject> getTempProjects() {
if (this.tempProjects == null) {
this.tempProjects = new ArrayList();
}
return this.tempProjects;
}
}
I can generate the xml using this code:
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[]{TempProjects.class});
jaxbContext.createMarshaller();
JaxbDataFormat jaxbDataFormat = new JaxbDataFormat(jaxbContext); //import org.apache.camel.converter.jaxb.JaxbDataFormat;
I call
.marshal(jaxbDataFormat)
in my route to effect the marshalling from the pojo to xml.
The generated xml is posted below:
<TempProjects xmlns="http://blah.blah/foo/schema/v2">
<TempProject>
<Ecode>1</Ecode>
<TempName>Tempname1</TempName>
</TempProject>
<TempProject>
<Ecode>2</Ecode>
<TempName>Tempname2</TempName>
</TempProject>
How can i generate a marshalled xml that will have a namespace like this...
<TempProjects xmlns:myprefix="http://blah.blah/foo/schema/v2">
Reason being why I needed a namespaceprefix is because I plan to split the values (e.g. Ecode) in the xml using xpath and I needed a namespaceprefix to do that (thats what ive researched, i might be wrong).
My planned code in my route is
.marshal(jaxbDataFormat)
.split( xpath("/TempProjects/TempProject/Ecode/text()").namespaces(ns1),
new ProjectIdsAggregator()) //the above xpath doesn't work because it doesn't have a namespace prefix
//Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" );
I looked at jaxbDataFormat.setNamespacePrefixRef("myprefix"), but i got an error (org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: myprefix of type: java.util.Map)
I'm actually quite new in the apache camel routing world, so i might be missing some basic stuff.
You don't need to change your XML at all. It is fine.
With the XML you posted and the Namespace declaration you posted, the following XPath works fine to split the XML (as an example) into two TempProject parts:
xpath("/myprefix:TempProjects/myprefix:TempProject").namespaces(ns1)
Because you declared the XML namespace like this:
Namespaces ns1 = new Namespaces("myprefix", "http://blah.blah/foo/schema/v2" )
Your XPath must use the prefix myprefix for all elements:
/myprefix:TempProjects/myprefix:TempProject

Getting Null value on complex type element using Moxy JAXB

This is my XML which I want to unmarshal (I do not have XSD for it). And I have to unmarshal it to get an array of names and Id's. So, names are getting generated just fine, but the issue is with the Id's. The Id's are set to null. Can anyone please help me identify the issue?
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" typeLanguage="http://www.w3.org/2001/XMLSchema">
<bpmn:collaboration id="Collaboration_0b6zt1k" isClosed="false">
<bpmn:participant id="Participant_113vq9r" processRef="CallActivity_00qe833" />
</bpmn:collaboration>
<bpmn:process id="CallActivity_00qe833" isClosed="false" isExecutable="true" name="Some Text" processType="None">
<bpmn:serviceTask activiti:class="Assign PC" completionQuantity="1" id="ServiceTask_0ip6tj7" implementation="##WebService" isForCompensation="false" name="Some Text 1" startQuantity="1">
<bpmn:extensionElements>
<activiti:properties>
<activiti:property name="specKey" value="ServiceTask_0ip6tj7" />
</activiti:properties>
</bpmn:extensionElements>
<bpmn:incoming>SequenceFlow_0sa9y9o</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1bd3qmp</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:serviceTask activiti:class="generateURL" completionQuantity="1" id="ServiceTask_11t11da" implementation="##WebService" isForCompensation="false" name="Some Text 2" startQuantity="1">
<bpmn:extensionElements>
<activiti:properties>
<activiti:property name="specKey" value="ServiceTask_11t11da" />
</activiti:properties>
</bpmn:extensionElements>
<bpmn:incoming>SequenceFlow_1bd3qmp</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_0cynzzs</bpmn:outgoing>
</bpmn:serviceTask>
<bpmn:serviceTask activiti:class="generateURL" completionQuantity="1" id="ServiceTask_11t11da" implementation="##WebService" isForCompensation="false" name="Some Text 3" startQuantity="1">
<bpmn:extensionElements>
<activiti:properties>
<activiti:property name="specKey" value="ServiceTask_11t11da" />
</activiti:properties>
</bpmn:extensionElements>
<bpmn:incoming>SequenceFlow_1bd3qmp</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_0cynzzs</bpmn:outgoing>
</bpmn:serviceTask>
</bpmn:process>
</bpmn:definitions>
This is my JAXB annotated class:
package XMLToObject;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class definitions {
#XmlPath("bpmn:process/bpmn:serviceTask/#name")
#XmlAttribute
List<String> name;
#XmlPath("bpmn:process/bpmn:serviceTask/#id")
#XmlAttribute
List<String> id;
#XmlAttribute
String typeLanguage;
public List<String> getname() {
return name;
}
public List<String> getid() {
return id;
}
public String gettypeLanguage() {
return typeLanguage;
}
}
This is my Java Class:
package XMLToObject;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Gauravb
*/
import java.io.File;
import java.util.Collection;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class XMLToObject {
public static void main(String[] args) {
try {
File file = new File("employee.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(definitions.class);
System.out.println("JAXB....."+jaxbContext.toString());
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
definitions p = (definitions) jaxbUnmarshaller.unmarshal(file);
System.out.println("Task Name:"+p.getname());
System.out.println("Svc ID:"+p.getid());
System.out.println("Type Language:"+p.gettypeLanguage());
} catch (JAXBException e) {e.printStackTrace(); }
}
}
Now, this is the output I am getting:
run:
JAXB.....org.eclipse.persistence.jaxb.JAXBContext#18eed359
Task Name:[Some Text 1, Some Text 2, Some Text 3]
Svc ID:null
Type Language:http://www.w3.org/2001/XMLSchema
BUILD SUCCESSFUL (total time: 0 seconds)
My issue is that why I am getting Svc ID as "null" when the same is working for Task Name.
Please note:
1. I am using MOXy for JAXB
2. Please note that when I change the sequence then names are getting set to null:
#XmlPath("bpmn:process/bpmn:serviceTask/#id")
#XmlAttribute
List<String> id;
#XmlPath("bpmn:process/bpmn:serviceTask/#name")
#XmlAttribute
List<String> name;
I am getting this output:
run:
JAXB.....org.eclipse.persistence.jaxb.JAXBContext#18eed359
Task Name:null
Svc ID:[ServiceTask_0ip6tj7, ServiceTask_11t11da, ServiceTask_11t11da]
Type Language:http://www.w3.org/2001/XMLSchema
BUILD SUCCESSFUL (total time: 0 seconds)
Looks like mulltiple attributes on Moxy is not supported (What a shame :( I just started to love MOXy)
Attached is a link
With MOXy and XPath, is it possible to unmarshal two lists of attributes?

JAXB unmarshalling object in object

I cant unmarshall xml because don't understand how to annotate object class in the another object. Please help.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<ODZ xmlns="http://www.company.com/1.0" >
<Data DataID="ZZZ">
<UserData UserKey="user_001">
<UserEvent>...</UserEvent>
</UserData>
</Data>
</ODZ>
Container classes:
I. First level with link to the second (ODZ -> Data).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "ODZ", namespace = "http://www.company.com/1.0")
public class ODZContainer {
private ImportContainer importContainer;
#XmlElement (name = "Data", type=ImportContainer.class)
public ImportContainer getImportContainer() {
return importContainer;
}
}
II. Second level with link to the third level(Data -> UserData).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "Data")
public class ImportContainer {
private String DataID;
private ArrayList<UserDataBean> userDataBean;
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAttribute(name = "DataID")
public String getDataID() {
return DataID;
}
}
III. Third level with link to the fourth level(UserData-> UserEvent).
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "UserData")
public class UserDataBean {
private ArrayList<UserEventBean> userEventData;
private String userEventID;
#XmlAttribute(name = "UserKey")
public String getUserEventID() {
return userEventID;
}
#XmlElement (name = "UserEvent", type=UserEventBean.class)
public ArrayList<UserEventBean> getUserEventBean() {
return userEventData;
}
}
The namespace qualification in your JAXB metadata does not match your XML. You can use the package level #XmlSchema annotation to specify the namespace qualification for your model.
#XmlSchema(
namespace = "http://www.company.com/1.0",
elementFormDefault = XmlNsForm.QUALIFIED)
package example;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information on JAXB and Namespaces
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
Notes About Your Metadata
Since the type of the ArrayList is already specified, you don't need to specify it via the #XmlElement annotation. It doesn't hurt, but its not necessary.
#XmlElement (name = "UserData", type=UserDataBean.class)
public ArrayList<UserDataBean> getUserDataBean() {
return userDataBean;
}
#XmlAccessorType(XmlAccessType.NONE) means that nothing is mapped unless it is explicitly annotated. This may or not be what you want. You may find the following article useful:
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html

How to use of XmlGregorianCalendar with fromJson and toJson methods of gson?

The theme of my project is to give XML format of data and get Json format using google-gson and I have JAXB generated java POJOs from XML schema in which I have a variable of XMLGregorianCalendar datatype.
I give the following input of XML and get the json format from the gson.toJson() method;
<?xml version="1.0" encoding="UTF-8"?>
<EmpRequest xmlns="http://java.com/Employee">
<EmplIn>
<EmpID>12</EmpID>
<Empname>sara</Empname>
<Designation>SA</Designation>
<DOJ>2002-05-30T09:30:10+06:00</DOJ>
</EmplIn>
</EmpRequest>
But in the output, I got the following.
{"emplIn":{"empID":"12","empname":"sara","designation":"SA","doj":{}}}
I surfed google and got the suggestion of adding in the xml schema and changing the XmlGregorianCalendar datatype with string. But I dont want to achieve it from both the ways.
I mean how to get the proper output with the XmlGregorianCalendar datatype through fromJson and toJson methods of gson?
Thank you so much,
Harish Raj.
Hopefully, This can fix my issue of using google-gson.
(The following should be added in where we create the object of Gson)
Step 1:
Gson gson =
new GsonBuilder().registerTypeAdapter(XMLGregorianCalendar.class,
new XGCalConverter.Serializer()).registerTypeAdapter(XMLGregorianCalendar.class,
new XGCalConverter.Deserializer()).create();
Step 2: And we need to create the XGCalConverter Class as like the following.
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import java.lang.reflect.Type;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
public class XGCalConverter
{
public static class Serializer implements JsonSerializer
{
public Serializer()
{
super();
}
public JsonElement serialize(Object t, Type type,
JsonSerializationContext jsonSerializationContext)
{
XMLGregorianCalendar xgcal=(XMLGregorianCalendar)t;
return new JsonPrimitive(xgcal.toXMLFormat());
}
}
public static class Deserializer implements JsonDeserializer
{
public Object deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext jsonDeserializationContext)
{
try
{
return DatatypeFactory.newInstance().newXMLGregorianCalendar(jsonElement.getAsString());
}
catch(Exception ex)
{
ex.printStackTrace();
return null;
}
}
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You could use MOXy to handle both the XML and JSON binding aspects of this use case. As I mentioned in my comment MOXy supports the XMLGregorianCalendar type. The Metadata would look like:
EmpRequest
package forum7725188;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="EmpRequest")
#XmlAccessorType(XmlAccessType.FIELD)
public class EmpRequest {
#XmlElement(name="EmplIn")
private EmplIn emplIn;
}
EmplIn
package forum7725188;
import javax.xml.bind.annotation.*;
import javax.xml.datatype.XMLGregorianCalendar;
#XmlAccessorType(XmlAccessType.FIELD)
public class EmplIn {
#XmlElement(name="EmpID")
private long empId;
#XmlElement(name="Empname")
private String name;
#XmlElement(name="Designation")
private String designation;
#XmlElement(name="DOJ")
private XMLGregorianCalendar doj;
}
package-info
#XmlSchema(namespace="http://java.com/Employee", elementFormDefault=XmlNsForm.QUALIFIED)
#XmlAccessorType(XmlAccessType.FIELD)
package forum7725188;
import javax.xml.bind.annotation.*;
Demo
You can configure the MOXy implementation of Marshaller to output JSON by setting the eclipselink.media-type property to be application/json.
package forum7725188;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(EmpRequest.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum7725188/input.xml");
EmpRequest empRequest = (EmpRequest) unmarshaller.unmarshal(xml);
JAXBElement<EmpRequest> jaxbElement = new JAXBElement<EmpRequest>(new QName(""), EmpRequest.class, empRequest);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(jaxbElement, System.out);
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<EmpRequest xmlns="http://java.com/Employee">
<EmplIn>
<EmpID>12</EmpID>
<Empname>sara</Empname>
<Designation>SA</Designation>
<DOJ>2002-05-30T09:30:10+06:00</DOJ>
</EmplIn>
</EmpRequest>
Output
{"EmplIn" :
{"EmpID" : "12",
"Empname" : "sara",
"Designation" : "SA",
"DOJ" : "2002-05-30T09:30:10+06:00"}}
For More Information
http://blog.bdoughan.com/2011/08/binding-to-json-xml-geocode-example.html
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/09/mapping-objects-to-multiple-xml-schemas.html

Resources