Local part cannot be "null" when creating a QName - java

We are trying to track down a bug. We get the above error in the logs.
Can anyone explain what this message means? Are there any typical reasons for getting this message?
The stacktrace is:
org.apache.axiom.om.OMException: java.lang.IllegalArgumentException: local part cannot be "null" when creating a QName
at org.apache.axiom.om.impl.builder.StAXOMBuilder.next(StAXOMBuilder.java:206)
at org.apache.axiom.om.impl.llom.OMNodeImpl.build(OMNodeImpl.java:318)
at org.apache.axiom.om.impl.llom.OMElementImpl.build(OMElementImpl.java:618)
at org.apache.axis2.jaxws.message.util.impl.SAAJConverterImpl.toOM(SAAJConverterImpl.java:147)
at org.apache.axis2.jaxws.message.impl.XMLPartImpl._convertSE2OM(XMLPartImpl.java:77)
at org.apache.axis2.jaxws.message.impl.XMLPartBase.getContentAsOMElement(XMLPartBase.java:203)
at org.apache.axis2.jaxws.message.impl.XMLPartBase.getAsOMElement(XMLPartBase.java:255)
at org.apache.axis2.jaxws.message.impl.MessageImpl.getAsOMElement(MessageImpl.java:464)
at org.apache.axis2.jaxws.message.util.MessageUtils.putMessageOnMessageContext(MessageUtils.java:202)
at org.apache.axis2.jaxws.core.controller.AxisInvocationController.prepareRequest(AxisInvocationController.java:370)
at org.apache.axis2.jaxws.core.controller.InvocationController.invoke(InvocationController.java:120)
at org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.invokeSEIMethod(JAXWSProxyHandler.java:317)
at org.apache.axis2.jaxws.client.proxy.JAXWSProxyHandler.invoke(JAXWSProxyHandler.java:148)

I got the same error message (local part cannot be "null" when creating a QName) while trying to construct a org.w3c.dom.Document from String. The problem went away after calling setNamespaceAware(true) on DocumentBuilderFactory. Working code snippet is given below.
private static Document getDocumentFromString(final String xmlContent)
throws Exception
{
DocumentBuilderFactory documentBuilderFactory =
DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
try
{
return documentBuilderFactory
.newDocumentBuilder()
.parse(new InputSource(new StringReader(xmlContent)));
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}

It means you are creating a DOM element or attribute using one of the namespace methods like createElementNS thus
document.createElementNS(namespace, null)
or createElementNS or setAttrbuteNS
and the second argument, the qname is null, or includes a prefix but no local part as in "foo:".
EDIT:
I would try to run the XML it's parsing through a validator. It's likely there's some tag or attribute name like foo: or foo:bar:baz that is a valid XML identifier but invalid according to the additional restrictions introduced by XML namespaces.

After whole hours in searching, I just wanted to share the Answer in this thread helped me with a Talend code migration - involving SOAP messages - from java8 to java11.
// Used libraries
import java.io.ByteArrayInputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.soap.SOAPBody;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
...
// This is the node I want to replace: "<DataArea><Contact/></DataArea>"
// <SOAP-ENV:Body> > SOAP Action (e.g. <ns:GetContact>) > <GetContactRequest> > <DataArea>
SOAPBody soapBodyRequest = objSOAPMessage.getSOAPPart().getEnvelope().getBody();
Node nodeDataArea = soapBodyRequest.getFirstChild().getFirstChild().getFirstChild();
// Build a valid Node object starting from a string e.g. "<Contact> etc etc nested-etc </Contact>"
DocumentBuilderFactory objDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
// As per java11, this is essential. It forces the Factory to consider the ':' as a namespace separator rather than part of a tag.
objDocumentBuilderFactory.setNamespaceAware(true);
// Create the node to replace "<DataArea><Contact/></DataArea>" with "<DataArea><Contact>content and nested tags</Contact></DataArea>"
Node nodeParsedFromString = objDocumentBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(strDataArea.getBytes())).getDocumentElement();
// Import the newly parsed Node object in the request envelop being built and replace the existing node.
nodeDataArea.replaceChild(
/*newChild*/nodeDataArea.getOwnerDocument().importNode(nodeParsedFromString, true),
/*oldChild*/nodeDataArea.getFirstChild()
);
This throws the Local part cannot be "null" when creating a QName exception if you don't put the .setNamespaceAware(true) instruction

Although this is an old thread, I hope this answer would help some one else searching this error.
I have faced the same error when I was trying to build a web app using maven-enunciate-cxf-plugin:1.28.
For me this was caused after I added a checked exception to my web service signature:
#WebMethod(operationName = "enquirySth")
public IBANResponse enquirySth(String input) throws I
InvalidInputException { ...
I have used JAX-WS Spec for exception throwing but no success. At the end I have found this issue in Enunciate issue tracker system which indicates this issue is resolved in current version, but I think it still exist.
Finally I have done following workaround to fix my problem:
adding #XmlRootElement to my FaultBean.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "FaultBean",propOrder = {"errorDescription", "errorCode"})
public class FaultBean implements Serializable {
#XmlElement(required = true, nillable = true)
protected String errorDescription;
#XmlElement(required = true, nillable = true)
protected String errorCode;
public FaultBean() {
}
public String getErrorDescription() {
return this.errorDescription;
}
public void setErrorDescription(String var1) {
this.errorDescription = var1;
}
public String getErrorCode() {
return this.errorCode;
}
public void setErrorCode(String var1) {
this.errorCode = var1;
}
}
That's it. Hope it helps.

Related

Mule upgrade 3.6 compiler error

I was working on mule 3.5.1, when I upgrade to mule 3.6 version, getting compile time error for following class:
import org.mule.module.jersey.MuleResponseWriter;
import com.sun.jersey.spi.container.ContainerResponse;
public class GLExportTransformer extends AbstractMessageTransformer {
public List<GLExport> methodType(#Payload MuleResponseWriter content){
List<GLExport> glExportList = (List<GLExport>) content;
System.out.println("Java payload is -->"+glExportList.getClass());
return glExportList ;
}
#Override
public Object transformMessage(MuleMessage message, String outputEncoding)throws TransformerException {
ContainerResponse cr = (ContainerResponse) message.getInvocationProperty("jersey_response");
List<GLExport> res = (List<GLExport>)cr.getResponse().getEntity();
System.out.println("Response from QB is -->"+res);
return res;
}
}
<custom-transformer name="StringToNameString" class="com.trinet.qb.utils.GLExportTransformer" doc:name="GL Export Transformer"/>
Compile time error:
The type org.mule.module.jersey.MuleResponseWriter is not visible
The import com.sun.jersey cannot be resolved
How do I resolve this?
In my Anypoint Studio shows Mule3.6 uses all jersey related jar uses 2.11 version of jar files. Using Java 1.7 version.
EDIT:
Here is my rest component(GLExportService):
#POST
#Path("/post")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public List<GLExport> postOperation(#Payload String content) throws ParseException {
System.out.println("Content from Reporting page-->\n\n"+content+"\n\n");
JSONParser jsonParser = new JSONParser();
Object jsonObjectInstance =null;
try {
jsonObjectInstance = jsonParser.parse(new StringReader(content));
} catch (ParseException e) {
System.out.println(e.getLocalizedMessage());
}
// parse json and assign to dto as glExportList
return glExportList;
Here is my mule flows:
<http:inbound-endpoint exchange-pattern="request-response" host="${hostname}" port="${glport}" path="QBJournalExport/QBGLRest" doc:name="HTTP"/>
<jersey:resources doc:name="REST">
<component class="com.qb.rest.GLExportService"/>
</jersey:resources>
<set-session-variable variableName="accessToken" value="#[payload.get(0).get('ACCESS_TOKEN')]" doc:name="Access token"/>
<set-session-variable variableName="accessTokenSecret" value="#[payload.get(0).get('ACCESS_TOKEN_SECRET')]" doc:name="Access Secret"/>
<set-session-variable variableName="realmId" value="#[payload.get(0).get('ACCT_SYSTEM_COMPANY_ID')]" doc:name="Company ID"/>
<set-session-variable variableName="quickbooksClient" value="#[com.qb.utils.QuickbooksUtils.getQuickbooksClient(sessionVars['accessToken'],sessionVars['accessTokenSecret'],'${consumerKey}','${consumerSecret}','${appToken}',sessionVars['realmId'])]" doc:name="QB Client"/>
<custom-transformer name="StringToNameString" class="com.qb.utils.GLExportTransformer" doc:name="GL Export Transformer"/>
<set-payload value="#[com.qb.utils.CreateJournalEntry.createJournalEntry(payload,sessionVars['accessToken'],sessionVars['accessTokenSecret'],'${consumerKey}','${consumerSecret}','${appToken}', sessionVars['realmId'])]" doc:name="Create Journal Entry"/>
import org.glassfish.jersey.server.ContainerResponse;
public class GLExportTransformer extends AbstractMessageTransformer {
#Override
public Object transformMessage(MuleMessage message, String outputEncoding)throws TransformerException {
ContainerResponse cr = (ContainerResponse) message.getInvocationProperty("jersey_response");
List<GLExport> res = (List<GLExport>)cr.getEntity();
return res;
}
}
method called methodType was a dummy code.
Try making the parameter type java.util.Object, and removing the import. You are immediately casting it to List<GLExport> anyway, so you don't appear to need that type.
We indeed made MuleResponseWriter package only, but that's not the root of your problem. In Mule 3.6 we upgraded from Jersey 1.6 to 2.11. Jersey 2 is quite different, it even includes a package rename from com.sun.jersey to org.glassfish.jersey. You can find more information about the upgrade in this post, including a link to Jersey's migration guide: http://blogs.mulesoft.org/mule-3-6-api/
What I don't understand anyway, is why you need to access the ContainerResponse in your transformer instead of having your jersey resource set the message payload to the List directly.
Regards
It seems they made the class org.mule.module.jersey.MuleResponseWriter package only.
This could be because the class was never part of the public Mule API or just minor typo.
Anyways, as it is right now there is no much you can do.
The only hack is to place the transformer and any class that references org.mule.module.jersey.MuleResponseWriter inside a package named:
org.mule.module.jersey
BUT THIS IS A HACK, and I wouldn't advise it.
I'll try to find out if was made on purpose and let you know ;)

xades4j.UnsupportedAlgorithmException: Unsupported transform on XML Signature provider

I'm trying to create a XAdES-BES signature for a given blob. For this signature, I'd need to add two transforms on the content before it is signed: Base64 (http://www.w3.org/2000/09/xmldsig#base64) & a custom one (called optional-deflate).
The problems lies with that optional transform. I'm trying to figure out how to implement a custom Transform, register it, and finally have Xades4J use it.
So far I figured a lot (thanks Google and a lot of time), so I got roughly to this: I've got a Provider-class that, in the constructor, puts the new TransformService; In my main code I add my Provider to the Security instance; then, I try to add the transform to my actual to-be-signed object.
Unfortunately, I always get the same error:
Exception in thread "main" xades4j.UnsupportedAlgorithmException: Unsupported transform on XML Signature provider (urn:xml:sig:transform:optional-deflate)
at xades4j.production.DataObjectDescsProcessor.processTransforms(DataObjectDescsProcessor.java:194)
at xades4j.production.DataObjectDescsProcessor.process(DataObjectDescsProcessor.java:87)
at xades4j.production.SignerBES.sign(SignerBES.java:173)
at xades4j.production.SignerBES.sign(SignerBES.java:122)
at com.mycompany.Test.createXades(Test.java:199)
at com.mycompany.Test.main(Test.java:47)
Caused by: org.apache.xml.security.transforms.TransformationException: Unknown transformation. No handler installed for URI urn:xml:sig:transform:optional-deflate
Original Exception was org.apache.xml.security.transforms.InvalidTransformException: Unknown transformation. No handler installed for URI urn:xml:sig:transform:optional-deflate
at org.apache.xml.security.transforms.Transforms.addTransform(Unknown Source)
at xades4j.production.DataObjectDescsProcessor.processTransforms(DataObjectDescsProcessor.java:185)
... 5 more
So, my code looks like this (abbreviated to what I think is necessary here):
TransformService class:
package com.mycompany.security;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import javax.xml.crypto.Data;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.TransformService;
import javax.xml.crypto.dsig.TransformException;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
public class OptionalDeflateTransform extends TransformService {
public AlgorithmParameterSpec getParameterSpec() {
return null;
}
public Data transform(Data data, XMLCryptoContext context) throws TransformException {
return null;
}
public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException {
return null;
}
public boolean isFeatureSupported(String feature) {
return false;
}
public void init(TransformParameterSpec params) throws InvalidAlgorithmParameterException {}
public void marshalParams(XMLStructure parent, XMLCryptoContext context) throws MarshalException {}
public void init(XMLStructure parent, XMLCryptoContext context) throws InvalidAlgorithmParameterException {}
}
Provider subclass:
package com.mycompany.security;
import java.security.Provider;
public final class OptionalDeflateProvider extends Provider {
private static final long serialVersionUID = 8849833178389029123L;
public OptionalDeflateProvider() {
super("OptionalDeflate", 1.0, "OptionalDeflate provider 1.0 implementing the OptionalDeflate transform algorithm.");
put("TransformService.urn:xml:sig:transform:optional-deflate", "com.mycompany.security.OptionalDeflateTransform");
}
}
And finally, my main Test class, which contains the actual signing. Without that transform, it works (but well, doesn't add the transform, which is necessary). So Base64 works.
protected static void createXades(String content) throws Exception {
/*Get certificate & private key*/
Certificates c = new Certificates();
c.initSession(); //some helper class where I can get my certificate & private key for signing
/*Create a document*/
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
Element objectElement = doc.createElement("object");
doc.appendChild(objectElement);
Element requestElement = doc.createElement("request");
requestElement.appendChild(doc.createTextNode(content));
requestElement.setAttribute("ID", UUID.randomUUID().toString());
objectElement.appendChild(requestElement);
/*Key provider, signing profile & signer itself*/
KeyingDataProvider kp = new CustomKeyingDataProvider(c.getCertificate(), c.getPrivateKey());
XadesSigningProfile p = new XadesBesSigningProfile(kp);
p.withAlgorithmsProviderEx(new ProviderEx());
XadesSigner signer = p.newSigner();
/*Add the optional deflate provider*/
Security.addProvider(new OptionalDeflateProvider());
System.out.println("--- installed providers ---");
for (Provider pr : Security.getProviders())
System.out.println(pr.getName());
System.out.println("---");
/*Test if we can get the transformservice-instance*/
TransformService ts = TransformService.getInstance("urn:xml:sig:transform:optional-deflate", "DOM");
System.out.println(ts.getAlgorithm());
System.out.println("---");
/*Signed data*/
DataObjectDesc flatFile = new DataObjectReference("#" + requestElement.getAttribute("ID"))
.withTransform(new GenericAlgorithm("http://www.w3.org/2000/09/xmldsig#base64"))
.withTransform(new GenericAlgorithm("urn:xml:sig:transform:optional-deflate"));
SignedDataObjects dataObjs = new SignedDataObjects(flatFile);
/*Actual signing*/
signer.sign(dataObjs, objectElement);
log(objectElement.getLastChild());
}
As you can see I print some things out. I for instance logged that the installation works fine and I also logged the installed providers. I get this as output:
--- installed providers ---
SUN
SunRsaSign
SunEC
SunJSSE
SunJCE
SunJGSS
SunSASL
XMLDSig
SunPCSC
SunMSCAPI
OptionalDeflate
---
urn:xml:sig:transform:optional-deflate
---
So as far as I can see, the provider has succesfully been registered, the transformservice can be loaded without a problem, ... So I don't really see what's going on?
I've checked the source code of Xades4j as well, and what's happening internally on the line .withTransform(new GenericAlgorithm("urn:xml:sig:transform:optional-deflate")) is pretty straight forward:
import org.apache.xml.security.transforms.Transforms;
...
private Transforms processTransforms(DataObjectDesc dataObjDesc, Document document) throws UnsupportedAlgorithmException {
Collection<Algorithm> dObjTransfs = dataObjDesc.getTransforms();
if (dObjTransfs.isEmpty()) {
return null;
}
Transforms transforms = new Transforms(document);
for (Algorithm dObjTransf : dObjTransfs) {
try {
List<Node> transfParams = this.algorithmsParametersMarshaller.marshalParameters(dObjTransf, document);
if (null == transfParams) {
transforms.addTransform(dObjTransf.getUri());
} else {
transforms.addTransform(dObjTransf.getUri(), DOMHelper.nodeList(transfParams));
}
} catch (TransformationException ex) {
throw new UnsupportedAlgorithmException("Unsupported transform on XML Signature provider", dObjTransf.getUri(), ex);
}
}
return transforms;
}
The exact line throwing up the error is transforms.addTransform(dObjTransf.getUri()). This transforms object is a 'standard' apache object (org.apache.xml.security.transforms.Transforms object). So I'd guess it should be able to get the same TransformService as I do in code litteraly two lines up higher? But it isn't?
Anyone that can point me out what I'm missing? I'll be eternally grateful.
Apparently, Apache Santuario loads transforms from a internal map. There is a register
method that you probably can use to register your custom transform.

How to get error details from JAXB Validator?

I have some classes with JAXB annotations, I have created some instances and I need to validate them against my XSD files. I should be able to get the details of what is wrong when the objects are invalid.
So far I haven't had luck, I know about this class ValidationEventHandler but apperantly I can use it with the Unmarshaller class, the problem is that I have to validate the objects not the raw XML.
I have this code:
MyClass myObject = new MyClass();
JAXBContext jaxbContext = JAXBContext.newInstance("x.y.z");
JAXBSource jaxbSource = new JAXBSource(jaxbContext, myObject);
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source schemaFile = new StreamSource(getClass().getClassLoader()
.getResourceAsStream("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);
Validator validator = schema.newValidator();
validator.validate(jaxbSource);
This code will work, it will validate the object and throw an exception with the message, something like this:
cvc-pattern-valid: Value '12345678901' is not facet-valid with respect
to pattern '\d{10}' for type 'id'.]
The problem is that I need specific details, with a string like that I would have to parse all the messages.
You can set an instance of ErrorHandler on the Validator to catch individual errors:
Validator validator = schema.newValidator();
validator.setErrorHandler(new MyErrorHandler());
validator.validate(source);
MyErrorHandler
Below is a sample implementation of the ErrorHandler interface. If you don't rethrow the exception the validation will continue.
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
public class MyErrorHandler implements ErrorHandler {
public void warning(SAXParseException exception) throws SAXException {
System.out.println("\nWARNING");
exception.printStackTrace();
}
public void error(SAXParseException exception) throws SAXException {
System.out.println("\nERROR");
exception.printStackTrace();
}
public void fatalError(SAXParseException exception) throws SAXException {
System.out.println("\nFATAL ERROR");
exception.printStackTrace();
}
}
For More Information
http://blog.bdoughan.com/2010/11/validate-jaxb-object-model-with-xml.html
I. If you validate a complex object hierarchy, you can create the Marshaller yourself and set its listener:
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setListener(yourListener);
JAXBSource source = new JAXBSource(marshaller, object);
This listener will get notified with instances of your objects as it walks the hierarchy.
II. Add an ErrorHandler from the other answer. At least with Wildfly 15 the messages look like:
cvc-maxInclusive-valid: Value '360.953674' is not facet-valid with respect to maxInclusive '180.0' for type '#AnonType_longitudeGeographicalPosition'.'
cvc-type.3.1.3: The value '360.953674' of element 'longitude' is not valid.'
So you can parse out the element name, which is the guilty terminal field name.
III. Combine I and II with some introspection and you can reconstruct a full Java Beans style path to the erroneous field if necessary.

Error transforming XML

I have a problem parsing an xml, actually transforming it.
The error I get is:
ERROR: 'Namespace for prefix 'SOAP-ENV' has not been declared.'
Jul 8, 2011 3:24:54 PM kumar.runs.start$2 run
SEVERE: null
javax.xml.transform.TransformerException: java.lang.RuntimeException: Namespace for prefix 'SOAP-ENV' has not been declared.
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:716)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313).........
The code I use is:
SAXParserFactory saxFactory = SAXParserFactory.newInstance();
SAXParser parser = saxFactory.newSAXParser();
XMLReader reader = new XMLTrimFilter(parser.getXMLReader());
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "no");
DOMResult result = new DOMResult();
SAXSource ss = new SAXSource(reader, is);
transformer.transform(ss, result);
return (Document)result.getNode();
XMLTrimFilter is custom implementation, extends XMLFilterImpl.
Also I came across this:
A Bug
but it is a rather old issue.
Does anybody have an idea how to fix it?
Thanks!
[Edit:
the xml:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<swp:addOwnRet xmlns:swbep="urn:SWBEP">
<apples>33</apples>
<bucket>
<orange>5</orange>
<banana>5</banana>
</bucket>
</swp:addOwnRet>
</SOAP-ENV:Body>
]
Edit 2:
XMLTrimFilter:
package kumar.srcs;
import java.io.CharArrayWriter;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLFilterImpl;
public class XMLTrimFilter extends XMLFilterImpl{
private CharArrayWriter contents = new CharArrayWriter();
public XMLTrimFilter(XMLReader parent){
super(parent);
}
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException{
writeContents();
super.startElement(uri, localName, qName, atts);
}
public void characters(char ch[], int start, int length){
contents.write(ch, start, length);
}
public void endElement(String uri, String localName, String qName) throws SAXException{
writeContents();
super.endElement(uri, localName, qName);
}
public void ignorableWhitespace(char ch[], int start, int length){}
private void writeContents() throws SAXException{
char ch[] = contents.toCharArray();
if(!isWhiteSpace(ch))
super.characters(ch, 0, ch.length);
contents.reset();
}
private boolean isWhiteSpace(char ch[]){
for(int i = 0; i<ch.length; i++){
if(!Character.isWhitespace(ch[i]))
return false;
}
return true;
}
}
We don't have enough information, but the first two things I would suspect are:
The input XML doesn't declare the namespace properly; i.e. it is invalid XML.
There is a bug in your custom XMLTrimFilter class.
The Sun bug is against a really old version of JAXP and was fixed a long time ago. And it doesn't much resemble your case ... to me.
The XML that you pasted is missing a namespace declaration, and will give errors if you try to parse it with a validating namespace aware XML parser. This could be the cause of your problems, though the error message doesn't seem right. A more likely cause is your custom filter, IMO.
After checking your XML in a suitable editor, I noticed that there's no namespace defined for the prefix "swp", the one that element addOwnRet falls under. It's possible that for SOAP usage purposes this is alright (I'm not very familiar with the protocol), but for an XSLT processor this is simply an XML document and nothing more.
Now, your exception said "namespace for prefix SOAP-ENV has not been declared". It says nothing about "swp". But it's not impossible that there's some bug in the exception reporting that puts the wrong prefix name in the message.
It would make sense that other processing doesn't fail, since an undeclared namespace prefix makes an XML document invalid, but doesn't necessarily make it non-well-formed. An XSLT processor must make use of namespace scopes to properly determine which templates an input node fits, so it requires the URI that the prefix is bound to.
If you can manually supply an XML document to your transformation, I suggest sending it through without that "swp" prefix, or simply declare some random namespace URI for it. Then see if this still happens. It's also possible that swbep should be used and the swp is a mistake.
The closing tag for the document is also missing, but I assume that simply fell off when pasting it into your post.

How to validate an XML file against an XSD file?

I'm generating some xml files that needs to conform to an xsd file that was given to me. How should I verify they conform?
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.
Here's how to do it using Xerces2. A tutorial for this, here (req. signup).
Original attribution: blatantly copied from here:
import org.apache.xerces.parsers.DOMParser;
import java.io.File;
import org.w3c.dom.Document;
public class SchemaTest {
public static void main (String args[]) {
File docFile = new File("memory.xml");
try {
DOMParser parser = new DOMParser();
parser.setFeature("http://xml.org/sax/features/validation", true);
parser.setProperty(
"http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation",
"memory.xsd");
ErrorChecker errors = new ErrorChecker();
parser.setErrorHandler(errors);
parser.parse("memory.xml");
} catch (Exception e) {
System.out.print("Problem parsing the file.");
}
}
}
We build our project using ant, so we can use the schemavalidate task to check our config files:
<schemavalidate>
<fileset dir="${configdir}" includes="**/*.xml" />
</schemavalidate>
Now naughty config files will fail our build!
http://ant.apache.org/manual/Tasks/schemavalidate.html
Since this is a popular question, I will point out that java can also validate against "referred to" xsd's, for instance if the .xml file itself specifies XSD's in the header, using xsi:schemaLocation or xsi:noNamespaceSchemaLocation (or xsi for particular namespaces) ex:
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.example.com/document.xsd">
...
or schemaLocation (always a list of namespace to xsd mappings)
<document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com/my_namespace http://www.example.com/document.xsd">
...
The other answers work here as well, because the .xsd files "map" to the namespaces declared in the .xml file, because they declare a namespace, and if matches up with the namespace in the .xml file, you're good. But sometimes it's convenient to be able to have a custom resolver...
From the javadocs: "If you create a schema without specifying a URL, file, or source, then the Java language creates one that looks in the document being validated to find the schema it should use. For example:"
SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();
and this works for multiple namespaces, etc.
The problem with this approach is that the xmlsns:xsi is probably a network location, so it'll by default go out and hit the network with each and every validation, not always optimal.
Here's an example that validates an XML file against any XSD's it references (even if it has to pull them from the network):
public static void verifyValidatesInternalXsd(String filename) throws Exception {
InputStream xmlStream = new new FileInputStream(filename);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(true);
factory.setNamespaceAware(true);
factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setErrorHandler(new RaiseOnErrorHandler());
builder.parse(new InputSource(xmlStream));
xmlStream.close();
}
public static class RaiseOnErrorHandler implements ErrorHandler {
public void warning(SAXParseException e) throws SAXException {
throw new RuntimeException(e);
}
public void error(SAXParseException e) throws SAXException {
throw new RuntimeException(e);
}
public void fatalError(SAXParseException e) throws SAXException {
throw new RuntimeException(e);
}
}
You can avoid pulling referenced XSD's from the network, even though the xml files reference url's, by specifying the xsd manually (see some other answers here) or by using an "XML catalog" style resolver. Spring apparently also can intercept the URL requests to serve local files for validations. Or you can set your own via setResourceResolver, ex:
Source xmlFile = new StreamSource(xmlFileLocation);
SchemaFactory schemaFactory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema();
Validator validator = schema.newValidator();
validator.setResourceResolver(new LSResourceResolver() {
#Override
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
InputSource is = new InputSource(
getClass().getResourceAsStream(
"some_local_file_in_the_jar.xsd"));
// or lookup by URI, etc...
return new Input(is); // for class Input see
// https://stackoverflow.com/a/2342859/32453
}
});
validator.validate(xmlFile);
See also here for another tutorial.
I believe the default is to use DOM parsing, you can do something similar with SAX parser that is validating as well saxReader.setEntityResolver(your_resolver_here);
Using Java 7 you can follow the documentation provided in package description.
// create a SchemaFactory capable of understanding WXS schemas
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// load a WXS schema, represented by a Schema instance
Source schemaFile = new StreamSource(new File("mySchema.xsd"));
Schema schema = factory.newSchema(schemaFile);
// create a Validator instance, which can be used to validate an instance document
Validator validator = schema.newValidator();
// validate the DOM tree
try {
validator.validate(new StreamSource(new File("instance.xml"));
} catch (SAXException e) {
// instance document is invalid!
}
If you have a Linux-Machine you could use the free command-line tool SAXCount. I found this very usefull.
SAXCount -f -s -n my.xml
It validates against dtd and xsd.
5s for a 50MB file.
In debian squeeze it is located in the package "libxerces-c-samples".
The definition of the dtd and xsd has to be in the xml! You can't config them separately.
With JAXB, you could use the code below:
#Test
public void testCheckXmlIsValidAgainstSchema() {
logger.info("Validating an XML file against the latest schema...");
MyValidationEventCollector vec = new MyValidationEventCollector();
validateXmlAgainstSchema(vec, inputXmlFileName, inputXmlSchemaName, inputXmlRootClass);
assertThat(vec.getValidationErrors().isEmpty(), is(expectedValidationResult));
}
private void validateXmlAgainstSchema(final MyValidationEventCollector vec, final String xmlFileName, final String xsdSchemaName, final Class<?> rootClass) {
try (InputStream xmlFileIs = Thread.currentThread().getContextClassLoader().getResourceAsStream(xmlFileName);) {
final JAXBContext jContext = JAXBContext.newInstance(rootClass);
// Unmarshal the data from InputStream
final Unmarshaller unmarshaller = jContext.createUnmarshaller();
final SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
final InputStream schemaAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(xsdSchemaName);
unmarshaller.setSchema(sf.newSchema(new StreamSource(schemaAsStream)));
unmarshaller.setEventHandler(vec);
unmarshaller.unmarshal(new StreamSource(xmlFileIs), rootClass).getValue(); // The Document class is the root object in the XML file you want to validate
for (String validationError : vec.getValidationErrors()) {
logger.trace(validationError);
}
} catch (final Exception e) {
logger.error("The validation of the XML file " + xmlFileName + " failed: ", e);
}
}
class MyValidationEventCollector implements ValidationEventHandler {
private final List<String> validationErrors;
public MyValidationEventCollector() {
validationErrors = new ArrayList<>();
}
public List<String> getValidationErrors() {
return Collections.unmodifiableList(validationErrors);
}
#Override
public boolean handleEvent(final ValidationEvent event) {
String pattern = "line {0}, column {1}, error message {2}";
String errorMessage = MessageFormat.format(pattern, event.getLocator().getLineNumber(), event.getLocator().getColumnNumber(),
event.getMessage());
if (event.getSeverity() == ValidationEvent.FATAL_ERROR) {
validationErrors.add(errorMessage);
}
return true; // you collect the validation errors in a List and handle them later
}
}
One more answer: since you said you need to validate files you are generating (writing), you might want to validate content while you are writing, instead of first writing, then reading back for validation. You can probably do that with JDK API for Xml validation, if you use SAX-based writer: if so, just link in validator by calling 'Validator.validate(source, result)', where source comes from your writer, and result is where output needs to go.
Alternatively if you use Stax for writing content (or a library that uses or can use stax), Woodstox can also directly support validation when using XMLStreamWriter. Here's a blog entry showing how that is done:
If you are generating XML files programatically, you may want to look at the XMLBeans library. Using a command line tool, XMLBeans will automatically generate and package up a set of Java objects based on an XSD. You can then use these objects to build an XML document based on this schema.
It has built-in support for schema validation, and can convert Java objects to an XML document and vice-versa.
Castor and JAXB are other Java libraries that serve a similar purpose to XMLBeans.
Using Woodstox, configure the StAX parser to validate against your schema and parse the XML.
If exceptions are caught the XML is not valid, otherwise it is valid:
// create the XSD schema from your schema file
XMLValidationSchemaFactory schemaFactory = XMLValidationSchemaFactory.newInstance(XMLValidationSchema.SCHEMA_ID_W3C_SCHEMA);
XMLValidationSchema validationSchema = schemaFactory.createSchema(schemaInputStream);
// create the XML reader for your XML file
WstxInputFactory inputFactory = new WstxInputFactory();
XMLStreamReader2 xmlReader = (XMLStreamReader2) inputFactory.createXMLStreamReader(xmlInputStream);
try {
// configure the reader to validate against the schema
xmlReader.validateAgainst(validationSchema);
// parse the XML
while (xmlReader.hasNext()) {
xmlReader.next();
}
// no exceptions, the XML is valid
} catch (XMLStreamException e) {
// exceptions, the XML is not valid
} finally {
xmlReader.close();
}
Note: If you need to validate multiple files, you should try to reuse your XMLInputFactory and XMLValidationSchema in order to maximize the performance.
Are you looking for a tool or a library?
As far as libraries goes, pretty much the de-facto standard is Xerces2 which has both C++ and Java versions.
Be fore warned though, it is a heavy weight solution. But then again, validating XML against XSD files is a rather heavy weight problem.
As for a tool to do this for you, XMLFox seems to be a decent freeware solution, but not having used it personally I can't say for sure.
Validate against online schemas
Source xmlFile = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream("your.xml"));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(Thread.currentThread().getContextClassLoader().getResource("your.xsd"));
Validator validator = schema.newValidator();
validator.validate(xmlFile);
Validate against local schemas
Offline XML Validation with Java
I had to validate an XML against XSD just one time, so I tried XMLFox. I found it to be very confusing and weird. The help instructions didn't seem to match the interface.
I ended up using LiquidXML Studio 2008 (v6) which was much easier to use and more immediately familiar (the UI is very similar to Visual Basic 2008 Express, which I use frequently). The drawback: the validation capability is not in the free version, so I had to use the 30 day trial.

Categories

Resources