Spring Batch - StaxEventItemReader handle invalid rows in xml - java

I'm using Spring Batch for reading XML files. And I want to validate records using XSD.
I'm able to run validations using setSchema but it will throw exception and kill whole job. My goal is to handle these invalid records, save them to log and skip them for final process.
My StaxEcentItemReader
#Bean
#JobScope
public StaxEventItemReader<?> reader() throws Exception {
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setClassesToBeBound(BookDto.class);
jaxb2Marshaller.setSchema(new ClassPathResource("book.xsd"));
jaxb2Marshaller.afterPropertiesSet();
return new StaxEventItemReaderBuilder<>()
.name("xmlReader")
.resource(new ClassPathResource("books.xml"))
.addFragmentRootElements("book")
.unmarshaller(jaxb2Marshaller)
.build();
}
XSD
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.test.com/xsd"
xmlns="http://www.test.com/xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="book" type="bookDto"/>
<xs:simpleType name="simAuthor">
<xs:restriction base="xs:string">
<xs:maxLength value="10"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="bookDto">
<xs:sequence>
<xs:element name="author" type="simAuthor" minOccurs="0"/>
<xs:element type="xs:float" name="price"/>
</xs:sequence>
<xs:attribute type="xs:string" name="id" use="required"/>
</xs:complexType>
</xs:schema>
Items
<?xml version="1.0"?>
<catalog>
<book xmlns="http://www.test.com/xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com/xsd" id="bk101">
<author>Gambardella, MatthewMatthewMatthewMatthewMatthewMatthewMatthewMatthew</author>
<price>44.95s</price>
</book>
<book xmlns="http://www.test.com/xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.com/xsd" id="bk102">
<author>Ralls, Kim</author>
<price>5.95</price>
</book>
</catalog>

I'm able to run validations using setSchema but it will throw exception and kill whole job.
You can use a fault-tolerant step and declare the exception as skippable. With this in place, invalid items will be skipped and the job will continue with next items instead of failing at the first invalid item. For more details, please refer to the Configuring Skip Logic section of the reference documentation.
My goal is to handle these invalid records, save them to log and skip them for final process.
For that, you need to register a SkipListener and log invalid items where needed.

Related

Can XSD Assertions be Used in Specifying a WSDL File

I have a WSDL file which also contains all types used in it (via <wsdl:types>
tag). When defining the types, I have something like this:
<wsdl:definitions name="service"
targetNamespace="http://www.xxx.yyy/reg/definitions" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tax="http://www.xxx.yyy/reg/definitions" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
.............
<xs:complexType name="CompanyName">
<xs:sequence>
<xs:element name="Name" type="xs:string" />
<xs:element name="ShortName" type="xs:string" minOccurs="0" />
</xs:sequence>
<xs:attribute name="Language" type="tax:LanguageType" use="required"/>
<xs:assert test="ShortName or #Language != 'Language1'"/>
</xs:complexType>
.............
</wsdl:definitions>
Unfortunately, it doesn't work giving the following exception when I try to start the application on Tomcat:
javax.xml.ws.WebServiceException: org.xml.sax.SAXParseException; s4s-elt-invalid-content.1: The content of 'CompanyName' is invalid. Element 'assert' is invalid, misplaced, or occurs too often.
The WSDL version is 1.2 and I don't know which version of xsd it uses when types are described in it, so I don't know if it is a xsd version (1.0 vs. 1.1) issue or something else.
Can someone help me in finding the real issue?
EDIT: I have added the header of the wsdl.
I have added the version attribute (version="1.1") to <xs:schema>
definition but that didn't help either:
<xs:schema targetNamespace="http://www.xxx.yyy/reg/definitions" elementFormDefault="qualified" version="1.1">
Looks like assertions were introduced with XML Schema v1.1 when the XMLSchema definition moved over to w3.org.
Sample header:
<wsdl:description
targetNamespace="http://www.w3.org/2002/ws/sawsdl/spec/wsdl/order#"
xmlns="http://www.w3.org/2002/ws/sawsdl/spec/wsdl/order#"
xmlns:wsdl="http://www.w3.org/ns/wsdl"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sawsdl="http://www.w3.org/ns/sawsdl">

JAXB with XPATH Flexibility to modify Objects at runtime

I want to modify JAXB loaded objects (from XML) so that i can marshall them back to the disk with updated XML.
Now lets take an example:
<Customers>
<Customer id="1" name="Jack">
<Address type="Residence">
<FirstLine>1 saxon Court</FirstLine>
<City>CY</City>
</Address>
</Customer>
<Customer id="2" name="Iain">
<Address type="Residence">
<FirstLine>104 Bank Road</FirstLine>
<City>NY</City>
</Address>
</Customer>
</Customers>
Now as I have multiple customers, I want to use XPATH functionality to get the handle of the Customer[#id=2] object where I can add/update an address.
If I will not use the XPATH like functionality then JAXB generated classes will have "List<Customer> customer" and I will have to iterate through the list of customer to match with my desirable Customer[#id=2].
Can anyone give me idea how to get the Object instance handle for the JAXB generated objects using XPATH, so I can marshall it back to update the actual XML on the disk.
If it cannot be done through JAXB, then what can be alternative solution to read and write XMLs using java Objects with XPATH flexibility.
Updated Question with Sample code:
Following is the sample code which shows what i want to achieve with Moxy/JAXB.
package org.soc.test.customers.moxy;
import java.io.File;
import java.util.List;
import javax.xml.bind.*;
public class UnmarshalDemo {
public static void main(String[] args) throws Exception {
org.eclipse.persistence.jaxb.JAXBContext jc = (org.eclipse.persistence.jaxb.JAXBContext) JAXBContext.newInstance(Customer.class);
File instanceDoc = new File("input.xml");
Customer customer = (Customer) jc.createUnmarshaller() .unmarshal(instanceDoc);
List<PhoneNumber> phones = jc.getValueByXPath(customer, "phone-number[#id=\"12\"]", null, List.class);
String customerId = jc.getValueByXPath(customer, "#id", null, String.class);
System.out.println("Customerid " + customerId + " , phone " + (phones==null?"0":phones.size()));
jc.setValueByXPath(customer, "phone-number[#id=\"12\"]/area-code/text()", null, "555");
jc.createMarshaller().marshal(customer, System.out);
}
}
Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<customer id="1141">
<first-name>Jon</first-name>
<last-name>Smith</last-name>
<phone-number id="11">
<area-code>515</area-code>
<number>2726652</number>
</phone-number>
<phone-number id="12">
<area-code>515</area-code>
<number>2726652</number>
</phone-number>
</customer>
XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified"
attributeFormDefault="unqualified">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="first-name" type="stringMaxSize5"/>
<xs:element name="last-name" type="stringMaxSize5"/>
<xs:element ref="phone-number" maxOccurs="2"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:element name="phone-number">
<xs:complexType>
<xs:sequence>
<xs:element name="area-code" type="stringMaxSize5"/>
<xs:element name="number" type="xs:string"/>
</xs:sequence>
<xs:attribute name="id" type="xs:string"/>
</xs:complexType>
</xs:element>
<xs:simpleType name="stringMaxSize5">
<xs:restriction base="xs:string">
<xs:maxLength value="5"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
jaxb.properties:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Output:
Customerid 1141 , phone 0
I am expecting the list size of the phone number to be 1 but it is returning null.
Hope this helps in understanding about the problem that I facing.
Thanks
Venkat
It's not supported with Moxy. The org.eclipse.persistence.internal.oxm.Context only supports numeric indexes in square brackets. While the XPathFragment understands it and creates appropriate predicates, the Context ignores those as far as searching for a match goes. I'd either raise a bug, or look for another tool.
See the Context source code (Commit 7cedaac6cdf9384ae9a06129d6f9abd607f9e3c4, Line 371 onwards) for exactly what's happening.

JAXB xjc:simple binding does not generate #XmlRootElement for base class

I am using JAXB 2.0. I have various elements and types defined in an XSD file. Here's an example:
<xs:element name="Person" type="Person" />
<xs:complexType name="Person">
<xs:attribute name="name" type="xs:string"/>
</xs:complexType>
<xs:element name="Musician" type="Musician"/>
<xs:complexType name="Musician">
<xs:complexContent>
<xs:extension base="Person">
<xs:attribute name="instrument" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="People" type="People"/>
<xs:complexType name="People">
<xs:sequence>
<xs:element name="person" type="Person" minOccurs="0" maxOccurs="Unbounded/>
</xs:sequence>
</xs:complexType>
So as you can see from the above schema example, we have a Person, who has a name, and a Musician, who is also a Person (though this may be subject to some debate, but that's for another forum). There is also a People element, which is essentially a collection of Person types.
I have the following in a bindings file:
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
xmlns:xjc= "http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc">
<jaxb:globalBindings optionalProperty="wrapper">
<xjc:simple/>
</jaxb:globalBindings>
My intended use of these objects is:
A Person may appear as a solitary Marshalled element, or may be a part of a People object
If a Person is part of a People object, then the xsi:type should indicate whether it's just a normal Person or a Musician.
So I need the generated Java classes to contain both an #XmlRootElement annotation as well as an #XmlType annotation. The xjc:simple binding creates both annotations for a Musician, but only creates the #XmlType for Person. So when I Marshall a Person object, all I get is:
<?xml version="1.0" encoding="UTF-8"?>
Whereas what I would like to see is:
<?xml version="1.0" encoding="UTF-8"?>
<Person name="John Doe"/>
For a People object, I want to see:
<?xml version="1.0" encoding="UTF-8"?>
<People>
<person name="John Doe" xsi:type="Person"/>
<person name="Keith Richards" xsi:type="Musician"/>
</People>
I have read about the simple binding with xjc, and it works with all the lowest levels in an inheritance hierarchy. However, the base classes end up without an #XmlRootElement annotation. For the use case I'm working on, it's imperative that base classes can be Marshalled as both a top-level element and as a member of other elements. Any suggestions would be welcome.
https://github.com/highsource/jaxb2-annotate-plugin could be used
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:annox="http://annox.dev.java.net"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.1"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
jaxb:extensionBindingPrefixes="xjc annox">
<jaxb:bindings node="//xs:complexType[#name='Person']">
<annox:annotate>
<annox:annotate annox:class="javax.xml.bind.annotation.XmlRootElement" name="person"/>
</annox:annotate>
</jaxb:bindings>
</jaxb:bindings>

JAXB support for SOAP style arrayType

I'm trying to make a new version of a server that previously used Axis 1.4 to respond to SOAP RPC requests using Spring-WS. I have a few of the RPC calls working, but I'm stuck trying to satisfy a request that expects a SOAP body that looks like this:
<rpcCallResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<responseElement soapenc:arrayType="xsd:string[5]"
xsi:type="soapenc:Array"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<responseElement xsi:type="xsd:string">val1</responseElement>
<responseElement xsi:type="xsd:string">val2</responseElement>
<responseElement xsi:type="xsd:string">val3</responseElement>
<responseElement xsi:type="xsd:string" xsi:nil="true"/>
<responseElement xsi:type="xsd:string" xsi:nil="true"/>
</responseElement>
</rpcCallResponse>
I'm struggling to write the XML schema for this, and to get the JAXB marshaller to shove the xsi:type annotations into the response.
What's the correct XML schema to use/set of annotations to use to get this to marhsal (Java -> XML) correctly?
One solution I found that works for getting the arrayType added is to, instead of deriving from the http://schemas.xmlsoap.org/soap/encoding/ schema, use a custom schema of the form:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://schemas.xmlsoap.org/soap/encoding/"
elementFormDefault="qualified">
<xs:attribute name="arrayType" type="xs:QName" />
</xs:schema>
...which replaces the type of the arrayType attribute with an xs:QName (vs. the actual type, which is just an xs:string). The advantage of using a QName seems to be that JAXB will take the QName's namespace and shove it onto the element when serialization happens -- which was the main blocker on getting a working schema above.
The schema for above now looks something like:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:encoding="http://schemas.xmlsoap.org/soap/encoding/"
targetNamespace="http://foo.com/bar"
elementFormDefault="qualified">
<xs:import namespace="http://schemas.xmlsoap.org/soap/encoding/" schemaLocation="soapenc.xsd" />
<xs:element name="rpcCallResponse">
<xs:complexType>
<xs:element name="responseElement">
<xs:complexType>
<xs:sequence>
<xs:element name="responseElement" maxOccurs="5" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:attribute ref="soapenc:arrayType" />
</xs:complexType>
</xs:element>
</xs:schema>

JAXB - Generate schema. Complex Type custom attribute

I need to generate following schema from java class using JAXB.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xdb="http://xmlns.oracle.com/xdb">
<xs:element name="test" type="test"/>
<xs:complexType name="testName" xdb:SQLType="WEBY_TEST_NAME">
<xs:sequence>
<xs:element minOccurs="0" name="date" type="xs:dateTime"/>
<xs:element name="id" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
How to add xdb:SQLType="WEBY_TEST_NAME" into complexType element using jaxb annotations ?
i done same try for generating the schema for different tags, but the names which include reserve words or else need to declare as
#XmlElement(name="class")
public String getClasss() {
return classs;
}
in pojo, so at time of coding it uses name which we provide, and in java program it uses the declared variables.
may be your declaration become
#XmlElement(name="xdb:SQLType")
public String getxdbSQLType() {
return xdbSQLType;
}

Categories

Resources