I have to build an XML file from a java object in a java maven project.
I have a class named CrossIndustryInvoice with XML annotations.
#Data
#Builder
#AllArgsConstructor #NoArgsConstructor
#XmlRootElement(name="CrossIndustryInvoice", namespace = NamespaceMapper.RSM_URI)
#XmlAccessorType(XmlAccessType.FIELD)
public
class CrossIndustryInvoice {
#XmlElement(name = "ExchangedDocumentContext", namespace = NamespaceMapper.RSM_URI)
private ExchangedDocumentContext exchangedDocumentContext;
#XmlElement(name = "ExchangedDocument", namespace = NamespaceMapper.RSM_URI)
private ExchangedDocument exchangedDocument;
#XmlElement(name = "SupplyChainTradeTransaction", namespace = NamespaceMapper.RSM_URI)
private SupplyChainTradeTransaction supplyChainTradeTransaction;
}
I also created a class named NamespaceMapper where i define the different namespaces used in my XML
public class NamespaceMapper extends NamespacePrefixMapper {
public static final String QDT_PREFIX = "qdt"; // DEFAULT NAMESPACE
public static final String QDT_URI = "urn:un:unece:uncefact:data:standard:QualifiedDataType:100";
public static final String RAM_PREFIX = "ram"; // DEFAULT NAMESPACE
public static final String RAM_URI = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100";
public static final String RSM_PREFIX = "rsm"; // DEFAULT NAMESPACE
public static final String RSM_URI = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100";
public static final String UDT_PREFIX = "udt"; // DEFAULT NAMESPACE
public static final String UDT_URI = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100";
public static final String XSI_PREFIX = "xsi"; // DEFAULT NAMESPACE
public static final String XSI_URI = "http://www.w3.org/2001/XMLSchema-instance";
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
if(QDT_PREFIX.equals(namespaceUri)) {
return QDT_PREFIX;
}
if(RAM_PREFIX.equals(namespaceUri)) {
return RAM_PREFIX;
}
if(RSM_PREFIX.equals(namespaceUri)) {
return RSM_PREFIX;
}
if(UDT_PREFIX.equals(namespaceUri)) {
return UDT_PREFIX;
}
if(XSI_PREFIX.equals(namespaceUri)) {
return XSI_PREFIX;
}
return suggestion;
}
#Override
public String[] getPreDeclaredNamespaceUris() {
return new String[] { QDT_URI, RAM_URI, RSM_URI, UDT_URI, XSI_URI };
}
}
I also created a package-info.java file
#XmlSchema(
elementFormDefault= XmlNsForm.QUALIFIED,
namespace="http://www.example.com/FOO",
xmlns={
#XmlNs(
prefix="ns1",
namespaceURI="http://www.example.com/FOO"),
#XmlNs(
prefix="qdt",
namespaceURI="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"),
#XmlNs(
prefix="ram",
namespaceURI="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"),
#XmlNs(
prefix="rsm",
namespaceURI="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"),
#XmlNs(
prefix="udt",
namespaceURI="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"),
#XmlNs(
prefix="xsi",
namespaceURI="http://www.w3.org/2001/XMLSchema-instance"),
}
)
package fr.dsidiff.app.ui.model.request.metadata;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
In my application, I do have a method that converts Java Object to XML String.
private String payloadToXml(CrossIndustryInvoice invoice) throws JAXBException {
//Create JAXB Context
JAXBContext ctx = null;
try {
ctx = JAXBContext.newInstance(CrossIndustryInvoice.class);
Marshaller jaxbMarshaller = ctx.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(invoice, sw);
return sw.toString();
} catch (JAXBException e) {
e.printStackTrace();
throw new JAXBException("Oops something went wrong");
}
}
My problem is that the XML i generate is invalid and namespaces are not matching.
here is a sample of XML I obtain :
<ns2:CrossIndustryInvoice xmlns="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:ns2="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:ns4="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ns3="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<ns2:ExchangedDocumentContext>
<BusinessProcessSpecifiedDocumentContextParameter>
<ID>A1</ID>
</BusinessProcessSpecifiedDocumentContextParameter>
<GuidelineSpecifiedDocumentContextParameter>
<ID>urn:factur-x.eu:1p0:basicwl</ID>
</GuidelineSpecifiedDocumentContextParameter>
</ns2:ExchangedDocumentContext>
While i should get something different:
<?xml version='1.0' encoding='UTF-8'?>
<rsm:CrossIndustryInvoice xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<rsm:ExchangedDocumentContext>
<ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:ID>A1</ram:ID>
</ram:BusinessProcessSpecifiedDocumentContextParameter>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:basicwl</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
I didn't pay attention to the import. In my pom.xml I added the following dependency:
<!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.0.1</version>
</dependency>
In my NamespaceMapper class I was using the following import
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
I replaced it with :
import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper;
Related
I have read every topic about the matter on Stack, checked if all the variable names are same as in XML, checked if adapter is correct, but still can't find the solution. I need to unmarshall xml file to an object. I am using JAXB and it works, except for the LocalDateTime field. The code:
#Getter
#Setter
#ToString
#NoArgsConstructor
#EqualsAndHashCode
#XmlRootElement(name = "Alert")
#XmlAccessorType(XmlAccessType.FIELD)
public class ParsedAlert {
#XmlElement(name = "abcDate", required = true)
#XmlJavaTypeAdapter(value = LocalDateTimeAdapter.class)
private LocalDateTime abcDate;
//other fields that work fine
static ParsedAlert unmarshallEmail(String path)
throws JAXBException {
JAXBContext context = JAXBContext.newInstance(ParsedAlert.class);
try {
return (ParsedAlert)
context.createUnmarshaller().unmarshal(new FileReader(path));
} catch (JAXBException | FileNotFoundException ex) {
throw new AlertException(
String.format("Message: [%s]", ex.getMessage()));
}
}
Here is adapter class:
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
private DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
#Override
public LocalDateTime unmarshal(String v) throws Exception {
return LocalDateTime.parse(v,dateFormat);
}
#Override
public String marshal(LocalDateTime v) {
return v.format(dateFormat);
}
}
Part of the XML file:
<?xml version="1.0" encoding="utf-8"?>
<Alert>
<abcDate>2015-08-29T14:00:18</abcDate>
</Alert
Result is:
ParsedAlert(abcDate=null)
But it should be:
ParsedAlert(abcDate=2015-08-29T14:00:18)
I'm converting a JSON file into an ArrayList and then to XML by using Jackson. It is displayed in Firefox but just as a normal String. By using the inspect element tool I get the whole formatted xml though. Which function can I use to display it correctly on the browser?
My method:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
}
Desired output in browser:
<myclass xmlns="">
<myclass>XyClass</ci>
<myname>XyName</ci>
...
</myclass>
Actual output:
XyClassXyName...
Quite simplified the class looks like this:
#JacksonXmlRootElement(localName ="MyClass")
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class MyClass {
#XmlElement(required = true)
private String class;
#XmlElement(required = true)
private String name;
//....
//standard constructor
public MyClass() { }
public CI(String class, String name){
this.class = class;
this.name = name;
}
public String getClass() {
return class;
}
public String getName() {
return name;
}
public void setClass(String class) {
this.class = class;
}
public void setName(String name) {
this.name = name;
}
}
Another weird thing is that I have the exact annotations in another class, trying the same thing with that and there the browser does not display anything... Thanks for any help.
Well my mistake derived from two things basically. Most important is the definiton of the XML Root Element (not only as annotation in your "MyClass"). Define a global String to create a XML Root element, otherwise your document won't be well-formed and the mistake "junk after document element" will be shown.
private String asXml ="<?xml version=\"1.0\" encoding=\"utf-8\"?><MyList>";
I've adapted the method to:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName("MyClass").writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
asXml += "</MyList>";
}
And don't forget to add the correct MediaType in your RestController:
#RequestMapping(value="/display", method=RequestMethod.GET, produces=MediaType.APPLICATION_XML_VALUE)
public #ResponseBody String getList(Model model) {
return service.getAsXmlString();
}
I'm very good in converting a model class to JSON Array or Object.
But i'm a noob when it comes to XML.
I want my final output to be like this
<Response>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>
</Response>
To achieve it i create a model class
#XmlRootElement(name = "Response")
public class Response {
private Say say = new Say();
public Say getSay() {
return say;
}
public void setSay(Say say) {
this.say = say;
}
#XmlRootElement(name = "Say")
static class Say {
#XmlAttribute
private String voice = "alice";
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
}
Now after converting it to XML with jersey my output was
<Response>
<say voice="alice">
<string>Thanks for trying our documentation. Enjoy!</string>
</say>
</Response>
I got a extra string tag. I'm not sure what attribute to set for the String so that it comes in the body.? Or is there any other way?
Also for say. The 'S' is not capitalised. How can i make it a capital letter?
Thanks in advance
By default properties and public fields will be mapped to elements. What you want to do is use #XmlValue to map the field to the element's value.
#XmlRootElement(name = "Say")
#XmlAccessorType(XmlAccessType.FIELD)
static class Say {
#XmlAttribute
private String voice = "alice";
#XmlValue
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
Note the use of #XmlAccessorType(XmlAccessType.FIELD). This is so the default behavior doesn't "doubly" attempt to map the property defined by the getter and setter. Alternatively, you could place the annotations on the getter, and leave out the the #XmlAccessorType
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Response>
<say voice="alice">Thanks for trying our documentation. Enjoy!</say>
</Response>
public class ResponseTest {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Response.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
UPDATE
but can i know why the 'S' in Say is not capitalised even though #XmlRootElement(name = "Say") is specified?
You need to specify the name with #XmlElement(name = "Say") on the property. If you don't the default naming will kick in.
#XmlElement(name = "Say")
public Say getSay() {
return say;
}
The XmlRootElement(name = "Say") is only for if the element is used as the root element. For instance this:
Response.Say response = new Response.Say();
marshaller.marshal(response, System.out);
Would give you this output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>
I'm currently (trying) to work with the amazon product API to search thourgh items.
I have the response in XML, but i have an exception in jaxb, maybe i missed something..
Here is the XML :
XML response from Amazon
I want to extract items informations, but i'm getting some trouble.
Item class:
#XmlRootElement(name="ItemSearchResponse")
public class AmazonItem
{
private String name;
private String asin;
private String price;
public AmazonItem()
{
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="Title")
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="ASIN")
public String getAsin()
{
return asin;
}
public void setAsin(String asin)
{
this.asin = asin;
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="FormattedPrice")
public String getPrice()
{
return price;
}
public void setPrice(String price)
{
this.price = price;
}
}
my builder :
public class AmazonItemBuilder
{
public AmazonItemBuilder()
{
}
public List<AmazonItem> build(InputStream response)
{
try
{
JAXBContext context = JAXBContext.newInstance(AmazonItem.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
AmazonItem newItem = (AmazonItem) unMarshaller.unmarshal(response);
System.out.println(newItem);
}
catch (JAXBException e)
{
e.printStackTrace();
}
return null;
}
}
the "response" come from a URL response.openStream();
OK i forgot the error -_-
javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://webservices.amazon.com/AWSECommerceService/2011-08-01", local:"ItemSearchResponse"). Expected elements are <{}ItemSearchResponse>
Thank you !
It appears that the XML document is namespace qualified. You can use the package level #XmlSchema location annotation to specify the namespace qualification for the entire document. Package level annotations go on a special call called package-info that looks like the following:
package-info
#XmlSchema(
namespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.your.pkg;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
I hope the top level class to covert the response to object should be "ItemSearchResponse", try creating a class with member variable "Items" which inturn will have another member object array "AmazonItem"
How I can print 'null' as field value, when marshalling the string?
Example: error and error_code are Strings, and i want to use 'null' as a value indicating that there is no value/errors happened on the server side.
{
"error_code": null,
"error": null
}
Today, I have to use EMPTY values, so that "error_code" or "error" these fields generally fall into json, and if they were not explicitly initialized as this.errorCode = StringUtils.EMPTY;
So today, I have next json:
{
"error_code": "",
"error": ""
}
This is how that looks in a code:
#XmlRootElement()
#XmlAccessorType(XmlAccessType.FIELD)
public class Response
{
#SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(Response.class);
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME)
private String errorCode;
// #XmlJavaTypeAdapter(CafsResponse.EmptyStringAdapter.class)
#XmlElement(name = Response.ERROR_FIELD_NAME)
private String errorMessage;
// Empty Constructor
public Response()
{
this.errorCode = StringUtils.EMPTY; // explicit initialization, otherwise error_code will not appear as part of json, how to fix this this ?
this.errorMessage = StringUtils.EMPTY;
}
etc...
// Empty Constructor
public Response()
{
this.errorCode = null; // this variant dosn't work either, and error_code again didn't get to json
this.errorMessage = null;
}
See, #XmlJavaTypeAdapter, i thought that this potentially could help me - but no :)
Instead of null value, i'm getting "null" as string.
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
{
"error_code": "null", // this is not whta i wanted to get.
"error": "null"
}
Any help on this? - ask me if something is not clear.
full list:
/**
* Empty string Adapter specifying how we want to represent empty strings
* (if string is empty - treat it as null during marhsaling)
*
*/
#SuppressWarnings("unused")
private static class EmptyStringAdapter extends XmlAdapter<String, String>
{
#Override
public String unmarshal(String str) throws Exception
{
return str;
}
#Override
public String marshal(String str) throws Exception
{
if (StringUtils.isEmpty(str))
{
return null;
}
return str;
}
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You could use MOXy as your JSON provider to support this use case. Below is an example:
Response
MOXy will marshal properties marked with #XmlElement(nillable=true) to the representation you are looking for
(see: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html).
package forum11319741;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
public static final String ERROR_FIELD_NAME = "error";
public static final String ERROR_CODE_FIELD_NAME = "error_code";
#XmlElement(name = Response.ERROR_CODE_FIELD_NAME, nillable = true)
private String errorCode;
#XmlElement(name = Response.ERROR_FIELD_NAME, nillable = true)
private String errorMessage;
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum11319741;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.json.include-root", false);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
Output
{
"error_code" : null,
"error" : null
}
MOXy and JAX-RS
You can use the MOXyJsonProvider class to enable MOXy as your JSON provider in your JAX-RS application (see: http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html).
package org.example;
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class CustomerApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(CustomerService.class);
return set;
}
}
For More Information
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html