JAXB Unmarshalling to Java POJO - java

I want to convert this XMl into java Object but nit getting how to make POJO class for it. I can not change this xml because it is comning from remote server as a request and I need all information that it contains in order to response.
<?xml version="1.0" encoding="utf-8"?>
<methodCall>
<methodName>name</methodName>
<params>
<param><value><struct>
<member>
<name>subscriberInput</name>
<value><string>678</string></value>
</member>
<member>
<name>language</name>
<value><string>en</string></value>
</member>
<member>
<name>sessionId</name>
<value><string>16414746570268014</string></value>
</member>
<member><name>msisdn</name><value><string>1234</string></value>
</member>
<member>
<name>newRequest</name>
<value><string>1</string></value>
</member>
<member>
<name>transactionId</name>
<value><string>0122716414746578950</string>
</value>
</member>
</struct>
</value>
</param>
</params>\</methodCall>

You could use the combination of the following tools:
Parse XML to JSON (https://www.convertjson.com/xml-to-json.htm)
Create POJOs from JSON (https://json2csharp.com/json-to-pojo)
The result is the next:
// import com.fasterxml.jackson.databind.ObjectMapper; // version 2.11.1
// import com.fasterxml.jackson.annotation.JsonProperty; // version 2.11.1
/* ObjectMapper om = new ObjectMapper();
Root root = om.readValue(myJsonString), Root.class); */
public class Value2{
public String string;
public Struct struct;
}
public class Member{
public String name;
public Value value;
}
public class Struct{
public List<Member> member;
}
public class Param{
public Value value;
}
public class Params{
public Param param;
}
public class MethodCall{
public String methodName;
public Params params;
}
public class Root{
public MethodCall methodCall;
}
I hope I was able to help you.

Pojo Approach
Taking this xml to https://codebeautify.org/xml-to-java-converter , it produces us ("quickly", "good") results:
We can copy&paste the generated class, but still need to refactor little:
un-public all classes (or move them to separate files)
complete getters/setters of Struct
But, we "suspect already": The problem is in the details and details are in the structure:
package com.example.simpleschema;
import java.util.ArrayList;
public class MethodCall {
private String methodName;
Params ParamsObject;
// Getter Methods
public String getMethodName() {
return methodName;
}
public Params getParams() {
return ParamsObject;
}
// Setter Methods
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setParams(Params paramsObject) {
this.ParamsObject = paramsObject;
}
}
class Params {
Param ParamObject;
// Getter Methods
public Param getParam() {
return ParamObject;
}
// Setter Methods
public void setParam(Param paramObject) {
this.ParamObject = paramObject;
}
}
class Param {
Value ValueObject;
// Getter Methods
public Value getValue() {
return ValueObject;
}
// Setter Methods
public void setValue(Value valueObject) {
this.ValueObject = valueObject;
}
}
class Value {
Struct StructObject;
// Getter Methods
public Struct getStruct() {
return StructObject;
}
// Setter Methods
public void setStruct(Struct structObject) {
this.StructObject = structObject;
}
}
class Struct {
ArrayList< Object> member = new ArrayList< Object>();
public ArrayList<Object> getMember() {
return member;
}
public void setMember(ArrayList<Object> member) {
this.member = member;
}
}
With (only) this in our pom:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.13.1</version> <!-- or newer -->
</dependency>
We can do that:
package com.example.xmltest.simple;
import com.example.simpleschema.MethodCall;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.io.IOException;
public class MainSimple {
public static void main(String[] args) throws IOException {
final XmlMapper xmlMapper = new XmlMapper();
MethodCall value = xmlMapper
.readValue(MainSimple.class.getResourceAsStream("/test.xml"), MethodCall.class);
System.out.println(value);
xmlMapper.writeValue(System.out, value);
}
}
Prints:
com.example.simpleschema.MethodCall#23529fee
<MethodCall><methodName>name</methodName><params><param><value><struct><member><member>transactionId</member><member><string>0122716414746578950</string></member></member></struct></value></param></params></MethodCall>
With JAXB
We need to an xsd first! We can:
ask our provider, if he would supply us... or
we generate it ourself...
A "quick" lookup leads us (nowadays, with open-source) to https://learn.microsoft.com/en-us/visualstudio/xml-tools/how-to-create-an-xml-schema-from-an-xml-document .
click-clack in vscode, and our xsd looks like:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="methodCall">
<xs:complexType>
<xs:sequence>
<xs:element name="methodName" type="xs:string" />
<xs:element name="params">
<xs:complexType>
<xs:sequence>
<xs:element name="param">
<xs:complexType>
<xs:sequence>
<xs:element name="value">
<xs:complexType>
<xs:sequence>
<xs:element name="struct">
<xs:complexType>
<xs:sequence>
<xs:element name="member" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="value">
<xs:complexType>
<xs:sequence>
<xs:element name="string" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Putting this test.xsd under src/main/xsd (standard loaction of codehaus:jaxb2-maven-plugin), and add the following to our pom (sorry #all gradel-users):
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version> <!-- this depends on org.glassfish.jaxb:2.3.2!! -->
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- The (target) package of your generated sources -->
<packageName>com.example.jaxbschema</packageName> <!-- .${project.version} -->
</configuration>
</plugin>
</plugins>
</build>
...
On the next mvn process-classes (or above/or accomplished by IDE), this will generate us some "fine" classes in src/target/generated-sources/jaxb/...
These are not compilable!! (Or the structure is too recursive, or it is a jaxb(impl)-bug, consider it as you like)
We can fix!
Manually ... but have to move the generated com.example.myschema to "main" sources, de-activate the jaxb-plugin, and fix the name clash caused by the most inner Value:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "value", propOrder = {
"string"
})
public static class Value2 { ...
... and resolving remaining compiler conflicts.
This (generated&adjusted model) seems still to be buggy at least for jackson.
With this, spring-boot and spring-oxm:
...
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Spring OXM: needs jaxb -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<!-- managed by spring-boot: 5.3.14 -->
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<!-- managed by spring-boo: 2.3.1 -->
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>runtime</scope>
<!-- managed by spring-boo: 2.3.5 -->
</dependency>
</dependencies>
...
We can:
// ...
import com.example.jaxbschema.MethodCall; // !! same as generated/refactored.
// ...
#SpringBootApplication
public class SpringMain {
public static void main(String[] args) {
SpringApplication.run(SpringMain.class, args);
}
#Bean
org.springframework.oxm.jaxb.Jaxb2Marshaller jaxb2Marshaller() {
Jaxb2Marshaller mler = new org.springframework.oxm.jaxb.Jaxb2Marshaller();
mler.setClassesToBeBound(MethodCall.class);
return mler;
}
#Bean
#Autowired
CommandLineRunner runner(
#Value("test.xml") Resource xmlFile,
org.springframework.oxm.jaxb.Jaxb2Marshaller jaxb2Marshaller
) {
return (args) -> {
MethodCall readValue2 = (MethodCall) jaxb2Marshaller.unmarshal(new StreamSource(xmlFile.getInputStream()));
jaxb2Marshaller.marshal(readValue2, new StreamResult(System.out));
};
}
}
...which prints:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><methodCall><methodName>name</methodName><params><param><value><struct><member><name>subscriberInput</name><value><string>678</string></value></member><member><name>language</name><value><string>en</string></value></member><member><name>sessionId</name><value><string>16414746570268014</string></value></member><member><name>msisdn</name><value><string>1234</string></value></member><member><name>newRequest</name><value><string>1</string></value></member><member><name>transactionId</name><value><string>0122716414746578950</string></value></member></struct></value></param></params></methodCall>
When Structure/XML is big/complex
..and we only need "few values" form that, we should consider a more effcient (parser based) approach...

Related

Spring SOAP. Cannot get access to wsdl file

I try to create soap service and I have started from Spring-WS and its reference doc. But I have some questions: When i deploy this app to tomcat, in log i see such message:
org.apache.catalina.core.ApplicationContext.log No Spring WebApplicationInitializer types detected on classpath
Then, where that app has deployed, i try to get access to wsdl file, but i got 404 not found. What did i do wrong?
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>spring-ws</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring-ws</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
spring-ws-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:sws="http://www.springframework.org/schema/web-services"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<context:component-scan base-package="soap"/>
<sws:annotation-driven/>
<sws:dynamic-wsdl id="service"
portTypeName="HumanResource"
locationUri="http://localhost:8080/service/"
targetNamespace="hr:">
<sws:xsd location="/WEB-INF/hr.xsd"/>
</sws:dynamic-wsdl>
</beans>
schema xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date"/>
<xs:element name="EndDate" type="xs:date"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:string"/>
<xs:element name="LastName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
and endpoint
package soap;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
#Endpoint
public class HolidayRequest {
private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
private XPathExpression<Element> startDateExpression;
private XPathExpression<Element> endDateExpression;
private XPathExpression<Element> firstNameExpression;
private XPathExpression<Element> lastNameExpression;
private HumanResourceService humanResourceService;
#Autowired
public void HolidayEndpoint(HumanResourceService humanResourceSerivce) {
this.humanResourceService = humanResourceSerivce;
Namespace namespace = Namespace.getNamespace("hr:", NAMESPACE_URI);
XPathFactory xPathFactory = XPathFactory.instance();
startDateExpression = xPathFactory
.compile("//hr:StartDate", Filters.element(), null, namespace);
endDateExpression = xPathFactory
.compile("//hr:EndDate", Filters.element(), null, namespace);
firstNameExpression = xPathFactory
.compile("//hr:FirstName", Filters.element(), null, namespace);
lastNameExpression = xPathFactory
.compile("//hr:LastName", Filters.element(), null, namespace);
}
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")
public void handleHolidayRequest(#RequestPayload Element holidayRequest)
throws Exception {
Date startDate = parseDate(startDateExpression, holidayRequest);
Date endDate = parseDate(startDateExpression, holidayRequest);
String name =
firstNameExpression.evaluateFirst(holidayRequest).getText()
+ " " + lastNameExpression.evaluateFirst(holidayRequest)
.getText();
humanResourceService.bookHoliday(startDate, endDate, name);
}
private Date parseDate(XPathExpression<Element> expression, Element element)
throws ParseException {
Element result = expression.evaluateFirst(element);
if (result != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(result.getText());
} else {
throw new IllegalArgumentException(
"Could not evaluate [" + expression + "] on [" + element
+ "]");
}
}
}
pom.xml
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.hr</groupId>
<artifactId>holidayService</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>holidayService Spring-WS Application</name>
<url>http://www.springframework.org/spring-ws</url>
<build>
<finalName>root</finalName>
</build>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>3.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1</version>
</dependency>
</dependencies>
also, there is "targetNamespace". What is it? Can i delete it? Thank you in advance!
Problem was in tomcat. It didn't deploy war file. After re-install all good

No URI javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"SearchAndList"). Expected elements are (none)

I am having trouble to unmarshall my data. I got the following error:
ERROR FsceClient - Error in getDataInMatches : unexpected element
(uri:"", local:"SearchAndList"). Expected elements are (none)
requested params:+COUNTRY=US+YR=2016+DIV=Ford+WB=122.0
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"SearchAndList"). Expected elements are (none)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:726)
This is my xml file:
<SearchAndList>
<fvd>
+COUNTRY=US+YR=2016+DIV=Ford+WB=122.0
</fvd>
<sol>
<rsi>
<sType>Ss</sType>
<mHave>true</mHave>
<toAr>0</toAr>
<toAr>0</toAr>
<toAr>22</toAr>
</rsi>
<rsi>
<sType>ssa</sType>
<mHave>true</mHave>
<toAr>77</toAr>
</rsi>
</sol>
<sol>
<rsi>
<sType>sve</sType>
<mHave>false</mHave>
<toAr>0</toAr>
<toAr>21</toAr>
</rsi>
</sol>
</SearchAndList>
This is encountered when the XSD schema does not contain element definitions and only contains class definitions (i.e. complex types).
e.g. for this XSD,
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="foo">
<xs:sequence>
<xs:element name="bar" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
The object factory created is like this:
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Foo createFoo() {
return new Foo();
}
}
BUT FOR THIS XSD:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="foo" type="foo" nillable="true"/>
<xs:complexType name="foo">
<xs:sequence>
<xs:element name="bar" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
The ObjectFactory class created by JAXB is:
#XmlRegistry
public class ObjectFactory {
private final static QName _Foo_QNAME = new QName("", "foo");
public ObjectFactory() {
}
public Foo createFoo() {
return new Foo();
}
#XmlElementDecl(namespace = "", name = "foo")
public JAXBElement<Foo> createFoo(Foo value) {
return new JAXBElement<Foo>(_Foo_QNAME, Foo.class, null, value);
}
}
You can see that the JAXBElement wrapper creation method is also added. With the second XSD, the unmarshaller knows what to do when it encounters a tag with name "foo". So if you have an XSD, add "element" definitions as well as the complex types.
----- EDIT----
The sample unmarshaller code:
JAXBContext jaxbContext = JAXBContext.newInstance(ObjectFactory.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Object result = ((JAXBElement<Object>) jaxbUnmarshaller.unmarshal(stream)).getValue();

How to add additional element to WADL?

I try to add an error object to response (with HTTP status 422). It works fine, but I want also to add the scheme of my error object to the automatically generated WADL.
Code:
JAX-B model classes:
#XmlRootElement(namespace = "http://www.test.com/test")
#XmlAccessorType(value = XmlAccessType.FIELD)
public class UnprocessableEntityError {
#XmlElement
private String key;
public String getKey() {
return key;
}
public void setKey(final String key) {
this.key = key;
}
}
#XmlRootElement(namespace = "http://www.test.com/")
public class TestModel {
}
JAX-RS resource class:
#Path("test")
public class TestResource {
#POST
public TestModel doSomething() {
throw new WebApplicationException("Error", Response.status(422).entity(new UnprocessableEntityError()).build());
}
}
CXF configuration:
<jaxrs:server address="/rest" id="test" staticSubresourceResolution="true">
<jaxrs:serviceBeans>
<ref bean="testResource" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<bean class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
</jaxrs:providers>
</jaxrs:server>
WADL:
<?xml version="1.0"?>
<application xmlns:prefix1="http://www.test.com/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://wadl.dev.java.net/2009/02">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:complexType name="testModel">
<xs:sequence/>
</xs:complexType>
</xs:schema>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.test.com/" targetNamespace="http://www.test.com/" elementFormDefault="unqualified" attributeFormDefault="unqualified">
<xs:import/>
<xs:element name="testModel" type="testModel"/>
</xs:schema>
</grammars>
<resources base="http://localhost:8080/test-app/services/rest/1">
<resource path="/test">
<method name="POST">
<response>
<representation mediaType="*/*" element="prefix1:testModel"/>
</response>
</method>
</resource>
</resources>
</application>
Is there any way, to add (just for documentation) an additional element to the grammar of an automatically generated WADL?
You can customize WADL automatic generation but is not very flexible. The general CXF documentation is here but is not helpful.
In the area of <grammars> you can include a custom XSD or link one
Define a WadlGenerator in CXF spring file, and include in the jaxrs provider. For example with books.xsd
<jaxrs:server address="/rest" id="test" >
<jaxrs:providers>
<ref bean="wadlGenerator" />
</jaxrs:providers>
</jaxrs:server>
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="schemaLocations" value="classpath:/books.xsd"/>
</bean>
Programmatically is also possible
WadlGenerator wg = new WadlGenerator();
wg.setSchemaLocations(Collections.singletonList("classpath:/books.xsd"));
The generated WADL will be like this
<application xmlns="http://wadl.dev.java.net/2009/02" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<grammars>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://superbooks" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://superbooks">
<xs:element name="thebook" type="tns:book"/>
<xs:complexType name="book">
<xs:sequence>
<xs:element minOccurs="0" name="chapter" type="xs:string"/>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</grammars>
Also, you can include a link
<bean id="wadlGenerator" class="org.apache.cxf.jaxrs.model.wadl.WadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>
WADL
<grammars>
<include href="http://books"/>
</grammars>
The full documentation of WadlGenerator. I have tested with CXF 2.7
EDITED
Custom WADL Generator
CXF WadlGenerator fills the 'grammars' section with XSD, external links or the autogenerated wadl, but do not allow to combine them. To add an external resource link to generated grammar is needed to create a custom WadlGenerator
<grammars>
<include href="http://books"/>
<!-- The autogenerated grammar-->
</grammars>
This is a fully functional class for CXF 2.7, but I hope it works at any later version because uses inheritance and rewrite only a minimal part of code. It concats externalLinks (if exists) with the autogenerated code or XSD
package com.wadl;
import java.net.URI;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.jaxrs.model.ResourceTypes;
import org.apache.cxf.jaxrs.model.wadl.WadlGenerator;
public class CustomWadlGenerator extends WadlGenerator {
private List<URI> externalSchemaLinks;
private static final Logger LOG = LogUtils.getL7dLogger(CustomWadlGenerator.class);
//Overwrite setExternalLink so that it is not used in the superclass
#Override
public void setExternalLinks(List<String> externalLinks) {
externalSchemaLinks = new LinkedList<URI>();
for (String s : externalLinks) {
try {
String href = s;
if (href.startsWith("classpath:")) {
int index = href.lastIndexOf('/');
href = index == -1 ? href.substring(9) : href.substring(index + 1);
}
externalSchemaLinks.add(URI.create(href));
} catch (Exception ex) {
LOG.warning("Not a valid URI : " + s);
externalSchemaLinks = null;
break;
}
}
}
private class ExternalSchemaWriter implements WadlGenerator.SchemaWriter {
private List<URI>links;
private UriInfo uriInfo;
private SchemaWriter writer;
public ExternalSchemaWriter(List<URI>links, UriInfo ui, SchemaWriter writer){
this.links = links;
this.uriInfo = ui;
this.writer = writer;
}
public void write(StringBuilder sb) {
//write links
for (URI link : links) {
try {
URI value = link.isAbsolute() ? link : uriInfo.getBaseUriBuilder().path(link.toString()).build(new Object[0]);
sb.append("<include href=\"").append(value.toString()).append("\"/>");
} catch (Exception ex) {
CustomWadlGenerator.LOG.warning("WADL grammar section will be incomplete, this link is not a valid URI : " + link.toString());
}
}
//concat with default writer
writer.write(sb);
}
}
#Override
protected SchemaWriter createSchemaWriter(ResourceTypes resourceTypes, JAXBContext context, UriInfo ui) {
SchemaWriter schemaCollectionWriter = super.createSchemaWriter(resourceTypes, context, ui);
if (externalSchemaLinks == null){
//default behaviour
return schemaCollectionWriter;
} else {
//use custom writer
return new ExternalSchemaWriter(externalSchemaLinks,ui,schemaCollectionWriter);
}
}
}
Set WadlGenerator in spring config
<bean id="wadlGenerator" class="com.wadl.CustomWadlGenerator">
<property name="externalLinks" value="http://books"/>
</bean>

Different parent child namespace prefix expected

I have two xsds from which i have generated jaxb pojos using xjc. And i have generated XMLs using jaxb marshaller with jaxb-impl-2.2.6
For this I have overriden NamespacePrefixMapper as MyNamespacePrefixMapper
Parent.xsd
<xs:schema targetNamespace="parent"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:Env="Parent"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:child="urn:xsd:child">
<xs:import namespace="urn:xsd:child" schemaLocation="child.xsd"/>
<xs:element name="parent">
<xs:complexType>
<xs:sequence>
<xs:element name="child" type="child:child1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
child.xsd
<xs:schema xmlns="urn:xsd:child" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:xsd:child">
<xs:element name="child" type="child1"/>
<xs:complexType name="child1">
<xs:sequence>
<xs:element name="Id" type="Max20Text"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="Max20Text">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
XmlMarshallingTest.java
import javax.xml.bind.*;
import javax.xml.stream.XMLStreamException;
import org.junit.Test;
import parent.Parent;
import xsd.child.Child1;
public class XmlMarshallingTest {
#Test
public void testXmlMarshalling() throws JAXBException, XMLStreamException{
Parent envelope = new Parent();
Child1 businessApplicationHeaderV01 = new Child1();
businessApplicationHeaderV01.setId("ABC123");
envelope.setChild(businessApplicationHeaderV01);
JAXBContext context = JAXBContext.newInstance(envelope.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new MyNamespacePrefixMapper());
marshaller.marshal(envelope, System.out);
}
}
MyNamespacePrefixMapper.java
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
public class MyNamespacePrefixMapper extends NamespacePrefixMapper {
#Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean arg2) {
if("Parent".equals(namespaceUri)) {
return "parentPrefix";
} else if("urn:xsd:child".equals(namespaceUri)) {
return "childPrefix";
}
return "defaultPrefix";
}
}
the generated xml is like
<parentPrefix:parent xmlns:childPrefix="urn:xsd:child" xmlns:parentPrefix="parent">
<parentPrefix:child>
<childPrefix:Id>ABC123</childPrefix:Id>
</parentPrefix:child>
</parentPrefix:parent>
Here, my problem is , I expect the xml to be look like following
<parentPrefix:parent xmlns:childPrefix="urn:xsd:child" xmlns:parentPrefix="parent">
<childPrefix:child>
<childPrefix:Id>ABC123</childPrefix:Id>
</childPrefix:child>
</parentPrefix:parent>
I expect the prefix of child tag to be "childPrefix" but it shows "parentPrefix"
The parent tag is well generated with prefix "parentPrefix"
Environment Description
Maven 3.0.4
Java version: 1.7.0_04
OS : windows 7
Your schema defines the parent element as having a child element named child in the parent schema's own targetNamespace, whose type happens to come from the child namespace. If you want the parent to use the child element that is defined in the child schema (and thus in the urn:xsd:child namespace) then instead of
<xs:element name="child" type="child:child1"/>
you need
<xs:element ref="child:child"/>

JAXB substitution group marshals but will not unmarshal correctly

I am implementing the OGC Web Feature Service and part of that is creating a feature schema that will inherit from the OGC schema. My service marshals the XML fine, but the client is unable to unmarshal the XML. I wrote a tester that illustrates the problem:
...
ObjectFactory wfsfactory = new ObjectFactory();
net.opengis.gml.v_3_1_1.ObjectFactory gmlfactory = new net.opengis.gml.v_3_1_1.ObjectFactory();
com.example.ObjectFactory exampleFactory = new com.example.ObjectFactory();
OgcJaxbManager manager = OgcJaxbManager.getInstance();
FeatureCollectionType featureCollection = wfsfactory
.createFeatureCollectionType();
FeaturePropertyType prop = gmlfactory.createFeaturePropertyType();
prop.setFeature(exampleFactory.createFoo(exampleFactory.createFoo()));
featureCollection.setFeatureMember(Arrays.asList(prop));
//marshal to XML
String xml = manager.marshal(wfsfactory
.createFeatureCollection(featureCollection));
log.info(xml);
//unmarshal back to object
FeatureCollectionType afterMarshal = (FeatureCollectionType) manager
.unmarshal(xml);
JAXBElement<? extends AbstractFeatureType> feature = afterMarshal
.getFeatureMember().get(0).getFeature();
if (feature == null) {
log.info("null");
} else {
log.info("not null");
}
...
Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:FeatureCollection xmlns:ns2="http://www.w3.org/1999/xlink" xmlns:ns1="http://www.opengis.net/gml" xmlns:ns4="http://www.opengis.net/wfs" xmlns:ns3="http://www.w3.org/2001/SMIL20/" xmlns:ns9="http://www.opengis.net/wms" xmlns:ns5="http://www.opengis.net/ows/1.1" xmlns:ns6="http://www.opengis.net/ogc" xmlns:ns10="http://example.com" xmlns:ns7="http://www.opengis.net/ows" xmlns:ns11="http://www.w3.org/2001/SMIL20/Language" xmlns:ns8="http://www.opengis.net/wcs/1.1.1">
<ns1:featureMember>
<ns10:foo>
<ns10:bar>0</ns10:bar>
</ns10:foo>
</ns1:featureMember>
</ns4:FeatureCollection>
null
Here is the OGC schema I am extending:
...
<element name="FeatureCollection" type="gml:FeatureCollectionType" substitutionGroup="gml:_Feature"/>
<!-- =========================================================== -->
<complexType name="FeatureCollectionType">
<annotation>
<documentation>Concrete generic feature collection.</documentation>
</annotation>
<complexContent>
<extension base="gml:AbstractFeatureCollectionType"/>
</complexContent>
</complexType>
<!-- =========================================================== -->
<complexType name="AbstractFeatureCollectionType" abstract="true">
<annotation>
<documentation>A feature collection contains zero or more features.</documentation>
</annotation>
<complexContent>
<extension base="gml:AbstractFeatureType">
<sequence>
<element ref="gml:featureMember" minOccurs="0" maxOccurs="unbounded"/>
<element ref="gml:featureMembers" minOccurs="0"/>
</sequence>
</extension>
</complexContent>
</complexType>
<!-- ===== property for feature association ==== -->
<element name="featureMember" type="gml:FeaturePropertyType"/>
<!-- ============================================================== -->
<complexType name="FeaturePropertyType">
<annotation>
<documentation>Container for a feature - follow gml:AssociationType pattern.</documentation>
</annotation>
<sequence minOccurs="0">
<element ref="gml:_Feature"/>
</sequence>
<attributeGroup ref="gml:AssociationAttributeGroup"/>
</complexType>
<!-- ============================================================== -->
<element name="_Feature" type="gml:AbstractFeatureType" abstract="true" substitutionGroup="gml:_GML"/>
<!-- =========================================================== -->
<complexType name="AbstractFeatureType" abstract="true">
<annotation>
<documentation>An abstract feature provides a set of common properties, including id, metaDataProperty, name and description inherited from AbstractGMLType, plus boundedBy. A concrete feature type must derive from this type and specify additional properties in an application schema. A feature must possess an identifying attribute ('id' - 'fid' has been deprecated).</documentation>
</annotation>
<complexContent>
<extension base="gml:AbstractGMLType">
<sequence>
<element ref="gml:boundedBy" minOccurs="0"/>
<element ref="gml:location" minOccurs="0">
<annotation>
<appinfo>deprecated</appinfo>
<documentation>deprecated in GML version 3.1</documentation>
</annotation>
</element>
<!-- additional properties must be specified in an application schema -->
</sequence>
</extension>
</complexContent>
</complexType>
...
Here is my schema:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsd:schema version="1.0" targetNamespace="http://example.com"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:example="http://example.com"
xmlns:gml="http://www.opengis.net/gml"
elementFormDefault="qualified">
<xsd:import namespace="http://www.opengis.net/gml"
schemaLocation="http://schemas.opengis.net/gml/3.1.1/base/gml.xsd"/>
<xsd:element name="foo" type="example:foo"
substitutionGroup="gml:_Feature"/>
<xsd:complexType name="foo">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element name="bar" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
Here is the POJO that xjc produces:
package com.example;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "foo", propOrder = { "bar" })
public class Foo extends AbstractFeatureType {
protected String bar;
...
Any help would be greatly appreciated.
I made this work, with two caveats.
You didn't post the ObjectContext for com.example, so I used a hand-coded ObjectContext. It is critical to include the
substitutionHeadNamespace="http://www.opengis.net/gml" and substitutionHeadName="_Feature"
values in the #XmlElementDecl for the factory method, otherwise I see the same symptoms, i.e. marshalling OK, unmarshalling is empty, but no exceptions.
com.example.ObjectContext looks like this:
package com.example;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() { }
public Foo createFoo() { return new Foo(); }
#XmlElementDecl(namespace="http://example.com",
name="foo",
substitutionHeadNamespace="http://www.opengis.net/gml",
substitutionHeadName="_Feature")
public JAXBElement<Foo> createFoo(Foo foo) {
return new JAXBElement<Foo>(new QName("http://example.com", "foo"), Foo.class, foo);
}
}
com.example.Foo looks like this, including main:
package com.example;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlAccessType;
import net.opengis.gml.v_3_1_1.AbstractFeatureType;
import net.opengis.gml.v_3_1_1.FeaturePropertyType;
import net.opengis.wfs.v_1_1_0.FeatureCollectionType;
import net.opengis.wfs.v_1_1_0.ObjectFactory;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "foo", propOrder = { "bar" })
public class Foo extends AbstractFeatureType {
#XmlElement
protected String bar = "0";
#Override
public Object createNewInstance() {
return new Foo();
}
public static void main(String[] args) throws JAXBException {
ObjectFactory wfsfactory = new ObjectFactory();
net.opengis.gml.v_3_1_1.ObjectFactory gmlfactory = new net.opengis.gml.v_3_1_1.ObjectFactory();
com.example.ObjectFactory exampleFactory = new com.example.ObjectFactory();
FeatureCollectionType featureCollection = wfsfactory
.createFeatureCollectionType();
FeaturePropertyType prop = gmlfactory.createFeaturePropertyType();
prop.setFeature(exampleFactory.createFoo(exampleFactory.createFoo()));
featureCollection.setFeatureMember(Arrays.asList(prop));
//marshal to XML
JAXBContext ctx = JAXBContext.newInstance(ObjectFactory.class, net.opengis.gml.v_3_1_1.ObjectFactory.class, com.example.ObjectFactory.class);
StringWriter sw =new StringWriter();
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(wfsfactory.createFeatureCollection(featureCollection), sw);
System.out.println(sw.toString());
//unmarshal back to object
JAXBElement<FeatureCollectionType> afterMarshal = (JAXBElement<FeatureCollectionType>)
ctx.createUnmarshaller().unmarshal(new StringReader(sw.toString()));
JAXBElement<? extends AbstractFeatureType> feature = afterMarshal
.getValue().getFeatureMember().get(0).getFeature();
if (feature == null) {
System.out.println("null");
} else {
System.out.println("not null");
}
}
}
And this is the output I get:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:FeatureCollection xmlns:ns2="http://www.w3.org/1999/xlink" xmlns:ns1="http://www.opengis.net/gml" xmlns:ns4="http://example.com" xmlns:ns3="http://www.opengis.net/wfs" xmlns:ns5="http://www.w3.org/2001/SMIL20/" xmlns:ns6="http://www.opengis.net/ogc" xmlns:ns7="http://www.opengis.net/ows" xmlns:ns8="http://www.w3.org/2001/SMIL20/Language">
<ns1:featureMember>
<ns4:foo>
<ns4:bar>0</ns4:bar>
</ns4:foo>
</ns1:featureMember>
</ns3:FeatureCollection>
not null
Good luck going forward!

Categories