Generating Header fields in XML file by BeanIO - java

I'm trying to generate the following xml file which has 2 fields as Header and the Repeating section "rec" node :
<?xml version="1.0" encoding="UTF-8"?>
<transaction>
<createDate>20160708</createDate>
<dlrCode>100<dlrCode/>
<rec>
<processDate>20190108</processDate>
<srcID/>10<srcID/>
</rec>
<rec>
<processDate>20190108</processDate>
<srcID/>11<srcID/>
</rec>
<rec>
<processDate>20190108</processDate>
<srcID/>12<srcID/>
</rec>
</transaction>
This is the mapping file which I created:
<?xml version="1.0" encoding="UTF-8"?>
<beanio xmlns="http://www.beanio.org/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.beanio.org/2012/03 http://www.beanio.org/2012/03/mapping.xsd">
<stream name="dist" format="xml" xmlName="transaction" >
<record name="HeaderRecord" class="com.myPackage.HeaderRecord" minOccurs="1" maxOccurs="1" order="1" >
<field name="createDate" format="yyyyMMdd" />
<field name="dlrCode" />
</record>
<record name="DisRecord" class="com.myPackage.Record" minOccurs="0" maxOccurs="unbounded" xmlName="rec" order="2">
<field name="processDate" format="yyyyMMdd"/>
<field name="srcID"/>
</record>
</stream>
</beanio>
But the problem is, it generates the header fields inside the HeaderRecord node like this:
<?xml version="1.0" encoding="UTF-8"?>
<transaction>
<HeaderRecord>
<createDate>20160708</createDate>
<dlrCode>100<dlrCode/>
</HeaderRecord>
<rec>
<processDate>20190108</processDate>
<srcID/>10<srcID/>
</rec>
<rec>
<processDate>20190108</processDate>
<srcID/>11<srcID/>
</rec>
<rec>
<processDate>20190108</processDate>
<srcID/>12<srcID/>
</rec>
</transaction>
Is there something misconfigured in the mapping file? How to achieve the desired output?

By using the xmlType="none" attribute you can control if an xml element should be produced or not. The xmlName is by default equal to the record name when you don't specify a xmlName attribute, see here. A record will always be mapped to an xml element and with the use of segments, you might be able to get the desired output.
Try this mapping file:
<stream name="dist" format="xml" xmlType="none" >
<record name="HeaderRecord" class="com.mypackage.HeaderRecord" minOccurs="1" maxOccurs="1" xmlName="transaction">
<segment name="dummy" xmlType="none">
<field name="createDate" format="yyyyMMdd" />
<field name="dlrCode" />
</segment>
</record>
I don't think it is 100% what you are looking for though.

Related

Add an node to an xml when the same node is present in the xml as an empty tag

<?xml version="1.0" encoding="UTF-8"?>
<RESPONSE>
<STATUS>
<SERVERTIME><![CDATA[15319123123262]]></SERVERTIME>
<TYPE><![CDATA[getData]]></TYPE>
<RESPONSE_VERSION><![CDATA[1]]></RESPONSE_VERSION>
<DATA_VERSION><![CDATA[1]]></DATA_VERSION>
<VALUE><![CDATA[1]]></VALUE>
<ERRORS />
<WARNINGS />
</STATUS>
<DATA>
<CONTENT RECORDID="f2e110aa8ca24aewq929d26b9fcf3108962">
<METARESOURCEPATH><![CDATA[sites/xyz/content/meta/INFORMATION/0/INF427/]]></METARESOURCEPATH>
<REVIEW_TIMESTAMP><![CDATA[2018-07-20 15:36:00 IST]]></REVIEW_TIMESTAMP>
<REVIEW_TIMESTAMP_MILLIS><![CDATA[1532081160000]]></REVIEW_TIMESTAMP_MILLIS>
<VIEWS>
<VIEW>
<NAME><![CDATA[xyz]]></NAME>
<REFERENCE_KEY><![CDATA[xyz]]></REFERENCE_KEY>
<GUID><![CDATA[6d024478feb441231661f163de62d6e]]></GUID>
<OBJECTID><![CDATA[001]]></OBJECTID>
<PARENTID />
<CHILDCOUNT><![CDATA[8]]></CHILDCOUNT>
</VIEW>
</VIEWS>
<CATEGORIES />
</CONTENT>
</DATA>
</RESPONSE>
I need to add an element to CATEGORIES WHICH IS OF THE FORMAT.
When ever i try to add a new category it does not accept it because it finds a duplicate categories tag. But when i try to remove that empty tag , that is also not accepted because getelementbyname only returns null for categories.
<CATEGORIES>
<CATEGORY>
<NAME><![CDATA[xyz]]></NAME>
<REFERENCE_KEY><![CDATA[xyz]]></REFERENCE_KEY>
<GUID><![CDATA[6d024478feb441231661f163de62d6e]]></GUID>
<OBJECTID><![CDATA[001]]></OBJECTID>
<PARENTID />
<CHILDCOUNT><![CDATA[8]]></CHILDCOUNT>
</CATEGORY>
</CATEGORIES>
How can this be done with max efficiency ?

Unmarshalling without unique node names

I am stuck trying to figure out how to unmarshall an XML file supplied by IBM Cognos.
The structure does not provide unique names for the different child nodes under the element but there is a block of metadata that defines the order of the values.
This is a simplified sample of the XML file.
<?xml version="1.0" encoding="utf-8"?>
<dataset xmlns="http://developer.cognos.com/schemas/xmldata/1/" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance">
<!--
<dataset
xmlns="http://developer.cognos.com/schemas/xmldata/1/"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://developer.cognos.com/schemas/xmldata/1/ xmldata.xsd"
>
-->
<metadata>
<item name="EmployeeID" type="xs:string" length="20"/>
<item name="firstName" type="xs:string" length="50"/>
<item name="lastName" type="xs:string" length="50"/>
</metadata>
<data>
<row>
<value>EMP1</value>
<value>Joe</value>
<value>Blogs</value>
</row>
<row>
<value>EMP2</value>
<value>Mary</value>
<value>Soap</value>
</row>
</data>
</dataset>
I'm using Spring OXM and Castor for this project and I have no control over the XML format as I am pulling it via a web service from a third party system.
Update : I'm not adverse to swapping out Castor for a different marshalling/unmarshalling library.
The magic of XSLT to the rescue. By running the provided XML through the following XSLT stylesheet I was able to create an XML file that I could then unmarshall correctly.
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cognos="http://developer.cognos.com/schemas/xmldata/1/">
<xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
<xsl:template match="/">
<xsl:element name="DataSet">
<xsl:for-each select="//*[name()='row']">
<xsl:variable name="row" select="position()" />
<xsl:element name="Row">
<xsl:for-each select="//*[name()='item']">
<xsl:variable name="elementName" select="#name" />
<xsl:variable name="index" select="position()" />
<xsl:element name="{translate($elementName,' ','_')}">
<xsl:value-of select="//cognos:row[$row]/cognos:value[$index]" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This transformed the XML file as follows
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DataSet>
<Row>
<EmployeeID>EMP1</EmployeeID>
<firstName>Joe</firstname>
<lastName>Blogs</lastName>
</Row>
<Row>
<EmployeeID>EMP2</EmployeeID>
<firstName>Mary</firstname>
<lastName>Soap</lastName>
</Row>
</DataSet>

How to merge more than one XSD file to one XSD file?

I am not master in XML and XSD.
Just want to know how I can merge more than one XSD file to one XSD file?
Thanks in Advance.
You can use import (different namespace) and include (same namespace) multiple times. redefine can also be used multiple times. It depends on what you mean by "merge."
See also http://www.herongyang.com/XML-Schema/Multiple-XSD-Schema-Document-Include-Redefine-Import.html or http://msdn.microsoft.com/en-us/library/ee254473%28v=bts.10%29.aspx.
Edit: redefine can be used multiple times (similar to include).
Examples (validated in Eclipse) follow. I used different namespace (as the "merging" target namespace) and element names where necessary:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/m"
xmlns:tns="http://www.example.org/m" elementFormDefault="qualified">
<!-- import: different (i.e. not target) namespace -->
<import namespace="http://www.example.org/a" schemaLocation="so20046640a.xsd"/>
<import namespace="http://www.example.org/b" schemaLocation="so20046640b.xsd"/>
<!-- include: same namespace -->
<include schemaLocation="so20046640c.xsd"/>
<include schemaLocation="so20046640d.xsd"/>
<!-- redefine: same namespace -->
<redefine schemaLocation="so20046640e.xsd"/>
<redefine schemaLocation="so20046640f.xsd"/>
</schema>
...a.xsd:
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/a"
xmlns:tns="http://www.example.org/a" elementFormDefault="qualified">
<element name="a" type="int"/>
</schema>
...b.xsd: Same as ...a.xsd but target namespace .../b
...c.xsd: Same as ...a.xsd but target namespace .../m
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/m"
xmlns:tns="http://www.example.org/m" elementFormDefault="qualified">
<element name="a" type="int"/>
</schema>
...d.xsd: Same as ...c.xsd but element name b.
...e.xsd: Same as ...c.xsd but element name e.
...f.xsd: Same as ...c.xsd but element name f.

How to rename complex type in a WSDL during stub generation

I am using wsimport
I need to rename a xs:complextype name=Address to prevent some build conflicts.
Here is a snippet of the WSDL:
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:ns="http://fedex.com/ws/rate/v13" xmlns:s1="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://fedex.com/ws/rate/v13" name="RateServiceDefinitions">
<types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://fedex.com/ws/rate/v13">
<xs:complexType name="Address">…</xs:complexType>
....
</types>
....
</definitions>
I am using an external binding file:
<jxb:bindings
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:ns="http://fedex.com/ws/rate/v13"
xmlns:s1="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.1"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<jxb:globalBindings>
<jxb:javaType name="java.util.Calendar" xmlType="xs:dateTime" parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime" printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
</jxb:globalBindings>
<jxb:bindings node="definitions/types/xs:schema/xs:complexType[#name='Address']/xs:complexType">
<!-- change java method name from addNumbers() to add() -->
<jxb:class name="FedExAddress"/>
</jxb:bindings>
</jxb:bindings>
When I execute the build I get the following message:
[wsimport] [ERROR] XPath evaluation of "definitions/types/xs:schema/xs:complexType[#name='Address']/xs:complexType" results in empty target node
[wsimport] line 14 of file:/Users/davidboyd/projects/heritage/hybris/bin/custom/cpdeliveryservice/fedex_binding.xml
I have looked at the following postings here, here and a couple of references as well reference 1 and reference 2
But do not understand as to why this is not working.
After taking a breather I was able to solve the issue my taking a second look at this.
So my resulting binding is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxws:bindings
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:jaxws="http://java.sun.com/xml/ns/jaxws"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb" jxb:version="2.1"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
wsdlLocation="/RateService_v13.wsdl"
>
<enableWrapperStyle>true</enableWrapperStyle>
<enableAsyncMapping>false</enableAsyncMapping>
<!-- convert all xs:dateTime to java type of Calendar -->
<jaxws:globalBindings>
<jxb:javaType name="java.util.Calendar" xmlType="xs:dateTime" parseMethod="javax.xml.bind.DatatypeConverter.parseDateTime" printMethod="javax.xml.bind.DatatypeConverter.printDateTime" />
</jaxws:globalBindings>
<!-- Rename Address to FedExAddress -->
<jaxws:bindings node="//xs:complexType[#name='Address']">
<jxb:class name="FedExAddress"/>
</jaxws:bindings>
</jaxws:bindings>

Spring and CastorMarshaller: add namespace to XML root

My Java application tries to get information from a webservice. The XML request needs to have the namespace specified in the XML root element (class name), but the namespace of the tags (class fields) need to be empty (null), otherwise the webservice rejects the request.
I have to use Spring 3.0 and Spring WS 2.0 with CastorMarshaller (currently using Castor version 1.3.1) to marshall/unmarshall my Java objects into/from XML.
Please note the __PREFIX__ and __NAMESPACE__ locations in following code snippets.
Desired marshalled output
(i.e. the desired generated SOAP request)
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
<soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<__PREFIX__:className xmlns:__PREFIX__="__NAMESPACE__">
<fieldName>fieldValue</fieldName>
</__PREFIX__:className>
</soap-env:Body>
</soap-env:Envelope>
Currently marshalled output (i.e. the generated SOAP request)
Not adding the namespace
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
<soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<className>
<fieldName>fieldValue</fieldName>
</className>
</soap-env:Body>
</soap-env:Envelope>
or adding the namespace to all elements
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" />
<soap-env:Body xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<__PREFIX__:className xmlns:__PREFIX__="__NAMESPACE__">
<__PREFIX__:fieldName xmlns:__PREFIX__="__NAMESPACE__">fieldValue</__PREFIX__:fieldName>
</__PREFIX__:className>
</soap-env:Body>
</soap-env:Envelope>
which are both rejected by the webservice.
My configuration
CastorMarshaller bean in applicationContext.xml
<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller">
<property name="mappingLocation" value="classpath:castor-mapping.xml" />
<property name="ignoreExtraAttributes" value="true" />
<property name="ignoreExtraElements" value="true" />
<property name="namespaceMappings">
<map>
<entry key="__PREFIX__" value="__NAMESPACE__" />
</map>
</property>
</bean>
Castor Mapping file castor-mapping.xml
Not adding the namespace (namespace specified in the castorMarshaller bean through namespaceMappings should be added to the root)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="some.package.ClassName">
<map-to xml="className">
<field name="fieldName" type="string">
<bind-xml name="fieldName" node="element" />
</field>
</class>
</mapping>
or adding the namespace to all elements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN"
"http://castor.org/mapping.dtd">
<mapping>
<class name="some.package.ClassName">
<map-to xml="className" ns-uri="__NAMESPACE__" ns-prefix="__PREFIX__">
<field name="fieldName" type="string">
<bind-xml name="fieldName" node="element" />
</field>
</class>
</mapping>
Since I am facing the same problem, a solution that I am considering is the following:
Create a interceptor extending EndpointInterceptorAdapter
Override handleResponse method
Modify to soap message by directly access or using a transformer
public class MyEndpointInterceptorAdapter extends
EndpointInterceptorAdapter{
#Override
public boolean handleResponse(MessageContext msgContext, Object endpoint) throws IOException {
WebServiceMessage responseMsg = msgContext.getResponse();
SoapMessage soapMsg = (SoapMessage) responseMsg;
if(soapMsg!=null){
SoapEnvelope soapEnvelope=soapMsg.getEnvelope();
if(soapEnvelope!=null){
SoapBody soapbody=soapEnvelope.getBody();
if(soapbody!=null){
Source bodySource=soapbody.getSource();
if(bodySource instanceof DOMSource){
DOMSource bodyDomSource=(DOMSource)bodySource;
Node bodyNode=bodyDomSource.getNode();
if(bodyNode!=null){
NodeList bodyNodeList=bodyNode.getChildNodes();
if(bodyNodeList.getLength()!=0){
Element root=(Element)bodyNodeList.item(0);
root.setAttribute("xmlns:ns", "YourURI");
root.setPrefix("ns");
}
}
}
}
}
}
return true;
}
}

Categories

Resources