Spring SOAP. Cannot get access to wsdl file - java

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

Related

JAXB Unmarshalling to Java POJO

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...

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"/>

Writing Rest client to call the RESt service?

I wrote RESt web service using JERSEY. PFB my end point.
Endpoint class:
package org.madbit.rest;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.madbit.rest.ws.SumRequest;
import org.madbit.rest.ws.SumResponse;
#Path("/services")
public class SumEndpoint {
#POST
#Path("sum")
#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_XML)
public SumResponse getSum(SumRequest request) {
SumResponse response = new SumResponse();
List<Integer> elements = request.getElement();
int sum = 0;
for (Integer element: elements)
sum += element;
response.setSum(sum);
return response;
}
}
XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.madbit.org/SumService" xmlns:tns="http://www.madbit.org/SumService" elementFormDefault="qualified">
<xs:element name="SumRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="element" type="xs:int" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="SumResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="sum" type="xs:int" minOccurs="1" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
I have generated POJOs from above xsd using Maven JAXB plugin. now i have SumRequest and SumResponse POJOs.
Now how can i write a Jersey client to get the response by passing below input?
<?xml version="1.0" encoding="ISO-8859-1"?>
<SumRequest xmlns="http://www.madbit.org/SumService">
<element>1</element>
<element>4</element>
</SumRequest>
Thanks!
This should work for you:
public static void testWS(){
try{
SumRequest sumRequest = new SumRequest(1,4); //here you have to create your input object
Client client = Client.create();
WebResource service = client.resource("http://www.madbit.org/SumService");
/*
* here you are calling the post method with your input object attached
*/
ClientResponse response = service.type(MediaType.APPLICATION_XML).post(ClientResponse.class, sumRequest);
SumResponse res = response.getEntity(SumResponse.class);
System.out.println("output JaxbWS:\n " + res.toString());
}
catch(Exception e){
System.out.println(e.getMessage());
}
I must add that your object SumRequest that you are passing will be automatically converted in XML because you method specifies that consumes XML.

Java XML Schema validation: prefix not bound

I have followed this tutorial for validating XML files. But I am receiving exception when validating XML file. What I am doing wrong? My codes:
XML schema:
<?xml version="1.0" encoding="utf-8" ?>
<!-- definition of simple elements -->
<xs:element name="first_name" type="xs:string" />
<xs:element name="last_name" type="xs:string" />
<xs:element name="phone" type="xs:string" />
<!-- definition of attributes -->
<xs:attribute name="type" type="xs:string" use="required"/>
<xs:attribute name="date" type="xs:date" use="required"/>
<!-- definition of complex elements -->
<xs:element name="reporter">
<xs:complexType>
<xs:sequence>
<xs:element ref="first_name" />
<xs:element ref="last_name" />
<xs:element ref="phone" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="report">
<xs:complexType>
<xs:attribute ref="type"/>
<xs:attribute ref="date" />
<xs:sequence>
<xs:element ref="reporter" />
</xs:sequence>
</xs:complexType>
</xs:element>
XML file to validate:
<?xml version="1.0" encoding="utf-8" ?>
<report type="5" date="2012-12-14">
<reporter>
<first_name>FirstName</firstname>
<last_name>Lastname</lastname>
<phone>+xxxxxxxxxxxx</phone>
</reporter>
</report>
Java source for validating:
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import org.xml.sax.SAXException;
import java.io.*;
public class ProtocolValidator
{
public static void main(String [] args) throws Exception
{
Source schemaFile = new StreamSource(new File("schema.xsd"));
Source xmlFile = new StreamSource(new File("test_xml.xml"));
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
try{
validator.validate(xmlFile);
System.out.println(xmlFile.getSystemId() + " is valid");
}
catch (SAXException e)
{
System.out.println(xmlFile.getSystemId() + " is NOT valid");
System.out.println("Reason: " + e.getLocalizedMessage());
}
}
}
Exception I am receiving:
Exception in thread "main" org.xml.sax.SAXParseException; systemId: file:/root/test/schema.xsd; lineNumber: 4; columnNumber: 50; The prefix "xs" for element "xs:element" is not bound.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)...
The XML schema file itself needs to be a valid XML document. You are missing the outer schema element and the namespace declaration for the xs prefix.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- schema elements here -->
</xs:schema>
Add this line to your schema, just below the first line:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
... and a closing tag as the last line in the schema:
</xs:schema>

XML validation against xsd's in Java

Issue:
We have several services that generate a fair amount of XML via XSLT. We don't have any XSD's. I have taken the time to create the XSD's and want to confirm they are correct. Currently I am attempting to verify that the XSD and the XML are validate correctly.
Problem:
I have an xsd(common.xsd) that is imported into all the xsd's. It is not publicly hosted yet, so only recently I found putting the full path of the common.xsd in the AccountList.xsd I was able to get further. I am now receiving the following:
org.xml.sax.SAXParseException; lineNumber: 9; columnNumber: 70; s4s-att-invalid-value: Invalid attribute value for 'type' in element 'element'. Recorded reason: UndeclaredPrefix: Cannot resolve 'common:response' as a QName: the prefix 'common' is not declared.
I am at a loss. I cannot find an example that has been asked in forums or a source code snippet that gets a success. I'd appreciate any assistance in getting this to successfully validate my xml.
common.xsd
<xs:schema version="1.0" elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns="http://www.myorg.com/xsd/gen_fin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.myorg.com/xsd/gen_fin">
<xs:complexType name="response">
<xs:sequence>
<xs:element name="code" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
AccountList.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema version="1.0" elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.myorg.com/xsd/accList"
targetNamespace="http://www.myorg.com/xsd/accList"
xmlns:common="http://www.myorg.com/xsd/gen_fin">
<xs:import namespace="http://www.myorg.com/xsd/gen_fin"
schemaLocation="/home/me/dev/projects/svn/myorg/xsd/src/main/resources/bg/gen_resp/common.xsd"/>
<xs:element name="fundamo">
<xs:complexType>
<xs:sequence>
<xs:element name="response" type="common:response" minOccurs="1" maxOccurs="1"/>
<xs:element name="transaction" type="tns:transaction" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="transaction">
<xs:sequence>
<xs:element name="transactionRef" type="xs:string"/>
<xs:element name="dateTime" type="xs:string"/>
<xs:element name="userName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Test.java
final InputStream commonXsdStream = getXsd(BG_GEN_RESP_XSD_PATH, COMMON);
ClassPathResource fullXsdListing = new ClassPathResource(BG_GEN_RESP_XSD_PATH);
File[] allXsds = fullXsdListing.getFile().listFiles();
for (File currentXsd : allXsds) {
final int filenameLength = currentXsd.getName().length();
final String filenameSanExt = currentXsd.getName().substring(0, filenameLength - 4);
if (!IGNORE.contains(filenameSanExt)) {
final InputStream xsltStream = getXslt(BG_GEN_RESP_XSLT_PATH, filenameSanExt);
final InputStream xsdStream = getXsd(BG_GEN_RESP_XSD_PATH, filenameSanExt);
TransformerFactory xmlTransformer = TransformerFactory.newInstance();
Templates xsltTemplate = xmlTransformer.newTemplates(new StreamSource(xsltStream));
final XSLToXMLConvertor converter = new XSLToXMLConvertor();
String generatedXml = converter.getXML(inputData, xsltTemplate);
LOG.info(generatedXml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(lnew StreamSource(xsdStream));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(generatedXml)));
/*
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
docBuilderFactory.setValidating(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
docBuilder.parse(new InputSource(new ByteArrayInputStream(generatedXml.getBytes("utf-8"))));
*/
}
}
}
It's usually a good idea to have a namespace and targetNamespace defined, although as Petru Gardea pointed out, not strictly necessary. Here's a combination that absolutely works:
AccountList.xsd
<xs:schema
version="1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.myorg.com/xsd/accList"
targetNamespace="http://www.myorg.com/xsd/accList"
xmlns:common="http://www.myorg.com/xsd/gen_fin">
<xs:import namespace="http://www.myorg.com/xsd/gen_fin" schemaLocation="common.xsd" />
<xs:element name="fundamo">
<xs:complexType>
<xs:sequence>
<xs:element name="response" type="common:response"
minOccurs="1" maxOccurs="1" />
<xs:element name="transaction" type="tns:transaction"
minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="transaction">
<xs:sequence>
<xs:element name="transactionRef" type="xs:string" />
<xs:element name="dateTime" type="xs:string" />
<xs:element name="userName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
common.xsd
<xs:schema
version="1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns="http://www.myorg.com/xsd/gen_fin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.myorg.com/xsd/gen_fin">
<xs:complexType name="response">
<xs:sequence>
<xs:element name="code" type="xs:string" />
<xs:element name="description" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
NewFile.xml (based on that schema):
<tns:fundamo xmlns:p="http://www.myorg.com/xsd/gen_fin"
xmlns:tns="http://www.myorg.com/xsd/accList" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.myorg.com/xsd/accList AccountList.xsd ">
<tns:response>
<p:code>p:code</p:code>
<p:description>p:description</p:description>
</tns:response>
</tns:fundamo>
ValidateXml.java:
import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ValidateXml {
/**
* #param args
*/
public static void main(String[] args) {
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder parser = documentBuilderFactory.newDocumentBuilder();
Document document = parser.parse(new File("NewFile.xml"));
Schema schema = schemaFactory.newSchema(new File("AccountList.xsd"));
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
} catch (SAXException e) {
e.printStackTrace();
} catch (ParserConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
You error related to "cannot find the declaration of element" is usually related to the XML document not being namespace-aware. Verify that your path to both XSDs is correct, and go back to the block of code where you build an XML document that is namespace-aware.

Categories