How to validate an XML file using Java with an XSD having an include?
you need to use an LSResourceResolver for this to work. please take a look at the sample code below.
a validate method:
// note that if your XML already declares the XSD to which it has to conform, then there's no need to declare the schemaName here
void validate(String xml, String schemaName) throws Exception {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder parser = builderFactory
.newDocumentBuilder();
// parse the XML into a document object
Document document = parser.parse(new StringInputStream(xml));
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// associate the schema factory with the resource resolver, which is responsible for resolving the imported XSD's
factory.setResourceResolver(new ResourceResolver());
// note that if your XML already declares the XSD to which it has to conform, then there's no need to create a validator from a Schema object
Source schemaFile = new StreamSource(getClass().getClassLoader()
.getResourceAsStream(schemaName));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
}
the resource resolver implementation:
public class ResourceResolver implements LSResourceResolver {
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
// note: in this sample, the XSD's are expected to be in the root of the classpath
InputStream resourceAsStream = this.getClass().getClassLoader()
.getResourceAsStream(systemId);
return new Input(publicId, systemId, resourceAsStream);
}
}
The Input implemetation returned by the resource resolver:
public class Input implements LSInput {
private String publicId;
private String systemId;
public String getPublicId() {
return publicId;
}
public void setPublicId(String publicId) {
this.publicId = publicId;
}
public String getBaseURI() {
return null;
}
public InputStream getByteStream() {
return null;
}
public boolean getCertifiedText() {
return false;
}
public Reader getCharacterStream() {
return null;
}
public String getEncoding() {
return null;
}
public String getStringData() {
synchronized (inputStream) {
try {
byte[] input = new byte[inputStream.available()];
inputStream.read(input);
String contents = new String(input);
return contents;
} catch (IOException e) {
e.printStackTrace();
System.out.println("Exception " + e);
return null;
}
}
}
public void setBaseURI(String baseURI) {
}
public void setByteStream(InputStream byteStream) {
}
public void setCertifiedText(boolean certifiedText) {
}
public void setCharacterStream(Reader characterStream) {
}
public void setEncoding(String encoding) {
}
public void setStringData(String stringData) {
}
public String getSystemId() {
return systemId;
}
public void setSystemId(String systemId) {
this.systemId = systemId;
}
public BufferedInputStream getInputStream() {
return inputStream;
}
public void setInputStream(BufferedInputStream inputStream) {
this.inputStream = inputStream;
}
private BufferedInputStream inputStream;
public Input(String publicId, String sysId, InputStream input) {
this.publicId = publicId;
this.systemId = sysId;
this.inputStream = new BufferedInputStream(input);
}
}
Java: string xml validation against xsd that include another xsd
Finally i found a solution. Thestandard method does not resolve included or imported files from the start. To resolve the included files i used the LSResourceResolver.
This works for me:
@Service
public class ResourceResolverImpl implements LSResourceResolver {
private ILoadFromSRService iLoadFromSRService;
@Autowired
public ResourceResolverImpl(ILoadFromSRService iLoadFromSRService){
this.iLoadFromSRService = iLoadFromSRService;
}
public LSInput resolveResource(String type,
String namespaceURI,
String publicId,
String systemId,
String baseURI) {
String string =iLoadFromSRService.getServiceBaseTypeSchema();
string = string.replace("\n", "").replace("\t", "");
InputStream resourceAsStream = new ByteArrayInputStream( string.getBytes());
return new LSInputImpl(publicId, systemId, resourceAsStream);
}
}
How to validate an XML file against an XSD file?
The Java runtime library supports validation. Last time I checked this was the Apache Xerces parser under the covers. You should probably use a javax.xml.validation.Validator.
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;
import java.net.URL;
import org.xml.sax.SAXException;
//import java.io.File; // if you use File
import java.io.IOException;
...
URL schemaFile = new URL("http://host:port/filename.xsd");
// webapp example xsd:
// URL schemaFile = new URL("http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd");
// local file example:
// File schemaFile = new File("/location/to/localfile.xsd"); // etc.
Source xmlFile = new StreamSource(new File("web.xml"));
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try {
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(xmlFile);
System.out.println(xmlFile.getSystemId() + " is valid");
} catch (SAXException e) {
System.out.println(xmlFile.getSystemId() + " is NOT valid reason:" + e);
} catch (IOException e) {}
The schema factory constant is the string http://www.w3.org/2001/XMLSchema
which defines XSDs. The above code validates a WAR deployment descriptor against the URL http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
but you could just as easily validate against a local file.
You should not use the DOMParser to validate a document (unless your goal is to create a document object model anyway). This will start creating DOM objects as it parses the document - wasteful if you aren't going to use them.
Validating xml against an xsd which has an include
You are experiencing a case problem. 'true' is a valid value for xsd:boolean, but 'True' (which is apparently what is in the XML document) is not.
The responses here (xsd:boolean element type accept "true" but not "True". How can I make it accept it?) provide some additional information and a possible alternate solution based on enumerated values (if you cannot change the True to true).
XML validation against XSD with javax.xml.validation.Validator: cannot resolve types come from 2nd XSD
I suspect that when the schema document is loaded using getResourceAsStream("schema/my.xsd");
it doesn't have a known base URI and therefore the relative URI xmldsig-core-schema.xsd
doesn't resolve properly. Try setting a base URI (systemId
property) on your StreamSource
object.
How do I validate an XML with a schema file that imports another schema file?
Have a look at the accepted answer for this post. Based on the little I can see in your post, and from what I can remember, the problem with the way you're loading the XSD has to do with the fact that doing it your way, the factory doesn't get a base uri, hence it can't apply its smarts to resolve references to the other two XSDs. I have to assume that your other two XSDs are also packed as resources in the same jar, in the same directory.
I have to admit that I am intrigued by the error message, which seems to imply the schema it loaded is valid, so it might be an XML issue; if the above doesn't help you, then you should consider posting the offending XML as well...
UPDATE: As per my comments, it is working as described. See code snippet below:
SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema compiledSchema = schemaFactory.newSchema(new SOTests().getClass()
.getClassLoader().getResource("generalInvoiceRequest_430.xsd"));
Validator validator = compiledSchema.newValidator();
try {
validator.validate(new StreamSource("D:\\...\\dentist_ersred_TG_430.xml"));
System.out.println("Valid.");
}
catch (SAXException ex) {
System.out.print("Not valid because..." + ex.getMessage());
}
You don't need to load more than the first XSD, and you shouldn't, since all the imports provide hints to the schema location.
The XSD files are all in the same directory in the generated jar, and they must be, since the relative URIs used for imports indicate same parent.
The program output with one of the files downloaded from here:
Valid.
And after introducing invalid content:
Not valid because...cvc-complex-type.2.4.a: Invalid content was found starting with element 'wrong'. One of '{"http://www.forum-datenaustausch.ch/invoice":processing}' is expected.
My recommendations to who reads this:
- use getResource() to get an URL; as I've said above, a StreamResource doesn't have a base URI, hence relative references as per your xsd:import schemaLocation attribute cannot be de-referenced.
- Let the schema/factory do the loading for you, when they're explicitly provided.
- make sure that when provided, relative references match the folder structure in your jar file.
Java XSD validation depends on order XSDs are provided
Javadoc of newSchema(Source[] schemas)
:
parsers may choose to ignore all but the first
<import>
for a given namespace
Read the full description in the javadoc for more context information.
So only the first file listed is used.
Since a.xsd
has an <xs:include>
for b.xsd
, they both get loaded when a.xsd
is first.
If b.xsd
is first, then only b.xsd
is loaded, which means that <xs:element name="TestMessage">
is missing, and hence the error.
XML String Validation with two XSD Strings (with include) / how does LSResourceResolver work?
Finally i found a solution.
This works for me:
@Service
public class ResourceResolverImpl implements LSResourceResolver {
private ILoadFromSRService iLoadFromSRService;
@Autowired
public ResourceResolverImpl(ILoadFromSRService iLoadFromSRService){
this.iLoadFromSRService = iLoadFromSRService;
}
public LSInput resolveResource(String type,
String namespaceURI,
String publicId,
String systemId,
String baseURI) {
String string =iLoadFromSRService.getServiceBaseTypeSchema();
string = string.replace("\n", "").replace("\t", "");
InputStream resourceAsStream = new ByteArrayInputStream( string.getBytes());
return new LSInputImpl(publicId, systemId, resourceAsStream);
}
}
Related Topics
Java Division by Zero Doesnt Throw an Arithmeticexception - Why
How to Combine Two Hashmap Objects Containing the Same Types
How to Import Spring-Config.Xml of One Project into Spring-Config.Xml of Another Project
Updating UI from Different Threads in Javafx
How to Convert a Byte Array into a Double and Back
Java: How to Import a Jar File from Command Line
How to Test to See If a Double Is Equal to Nan
Java.Io.Ioexception: Mark/Reset Not Supported
Why Equal Operator Works for Integer Value Until 128 Number
Java Recursive Fibonacci Sequence
Case Insensitive String as Hashmap Key
Java Simpledateformat for Time Zone with a Colon Separator
How to Generate Cdata Block Using Jaxb