XML validation in Java - why does this fail? - java

first time dealing with xml, so please be patient. the code below is probably evil in a million ways (I'd be very happy to hear about all of them), but the main problem is of course that it doesn't work :-)
public class Test {
private static final String JSDL_SCHEMA_URL = "http://schemas.ggf.org/jsdl/2005/11/jsdl";
private static final String JSDL_POSIX_APPLICATION_SCHEMA_URL = "http://schemas.ggf.org/jsdl/2005/11/jsdl-posix";
public static void main(String[] args) {
System.out.println(Test.createJSDLDescription("/bin/echo", "hello world"));
}
private static String createJSDLDescription(String execName, String args) {
Document jsdlJobDefinitionDocument = getJSDLJobDefinitionDocument();
String xmlString = null;
// create the elements
Element jobDescription = jsdlJobDefinitionDocument.createElement("JobDescription");
Element application = jsdlJobDefinitionDocument.createElement("Application");
Element posixApplication = jsdlJobDefinitionDocument.createElementNS(JSDL_POSIX_APPLICATION_SCHEMA_URL, "POSIXApplication");
Element executable = jsdlJobDefinitionDocument.createElement("Executable");
executable.setTextContent(execName);
Element argument = jsdlJobDefinitionDocument.createElement("Argument");
argument.setTextContent(args);
//join them into a tree
posixApplication.appendChild(executable);
posixApplication.appendChild(argument);
application.appendChild(posixApplication);
jobDescription.appendChild(application);
jsdlJobDefinitionDocument.getDocumentElement().appendChild(jobDescription);
DOMSource source = new DOMSource(jsdlJobDefinitionDocument);
validateXML(source);
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(new StringWriter());
transformer.transform(source, result);
xmlString = result.getWriter().toString();
} catch (Exception e) {
e.printStackTrace();
}
return xmlString;
}
private static Document getJSDLJobDefinitionDocument() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = null;
try {
builder = factory.newDocumentBuilder();
} catch (Exception e) {
e.printStackTrace();
}
DOMImplementation domImpl = builder.getDOMImplementation();
Document theDocument = domImpl.createDocument(JSDL_SCHEMA_URL, "JobDefinition", null);
return theDocument;
}
private static void validateXML(DOMSource source) {
try {
URL schemaFile = new URL(JSDL_SCHEMA_URL);
Sche maFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(schemaFile);
Validator validator = schema.newValidator();
DOMResult result = new DOMResult();
validator.validate(source, result);
System.out.println("is valid");
} catch (Exception e) {
e.printStackTrace();
}
}
}
it spits out a somewhat odd message:
org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'JobDescription'. One of '{"http://schemas.ggf.org/jsdl/2005/11/jsdl":JobDescription}' is expected.
Where am I going wrong here?
Thanks a lot

I think you are missing the namespace on your elements. Rather than calling createElement(), you can try
document.createElementNS(JSDL_SCHEMA_URL, elementName)
If necessary, you may need to use a prefix, e.g.
document.createElementNS(JSDL_SCHEMA_URL, "jsdl:"+elementName)

Related

Generate in and out xsd(schema) for each operation from the wsdl

Generate in and out XSD(schema) for each operation from the WSDL using java.
I have a WSDL file and want to generate the separate in and out XSD(schema) for every operation. I have used the soupUI jar for the same.
Below is my WSDL and the code to generate the XSD(schema) for every operation.
Please find the WSDL at http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl
Here is the code I have used:
public class SOAPInputGenerator {
public static void main(String[] args) throws Exception {
final String INPUT_PARAMETERS = ">?<";
WsdlProject project = new WsdlProject();
WsdlInterface[] wsdls = WsdlImporter.importWsdl(project, "http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl");
WsdlInterface wsdl = wsdls[0];
for (Operation operation : wsdl.getOperationList()) {
WsdlOperation wsdlOperation = (WsdlOperation) operation;
String operationName = wsdlOperation.getName();
System.out.println(wsdlOperation.createRequest(true));
String requestString = wsdlOperation.createRequest(true);
Document doc = getDocumentFromSoapRequestString(requestString);
Element docElement = doc.getDocumentElement();
System.out.println(docElement.getTagName());
NamedNodeMap allAttributes = docElement.getAttributes();
NodeList headerElements =
docElement.getElementsByTagName("soap:Header").item(0) != null ? docElement.getElementsByTagName("soap:Header") : docElement.getElementsByTagName("soapenv:Header");
System.out.println("SOAP header :"+headerElements.item(0).getNodeName());
docElement.removeChild(headerElements.item(0));
if (docElement != null && allAttributes != null) {
while(allAttributes.getLength() != 0)
{
Node attr = allAttributes.item(0);
System.out.println("*************Node : ******"+attr.getNodeName());
docElement.removeAttribute(attr.getNodeName());
}
}
String simpleXmlStr = returnDocumentInString(doc);
simpleXmlStr = simpleXmlStr.replaceAll("soapenv", "Operation").replaceAll("soap", "Operation").replaceAll(":", "");
System.out.println(simpleXmlStr);
if (simpleXmlStr.contains(INPUT_PARAMETERS)) {
generateXsd(operationName, simpleXmlStr);
}
}
}
public static String returnDocumentInString(Document doc) {
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer;
try {
transformer = tf.newTransformer();
transformer.transform(domSource, result);
} catch (TransformerConfigurationException transformerConfigurationException) {
System.out.println(transformerConfigurationException.getLocalizedMessage());
}
catch (TransformerException transformerException) {
System.out.println(transformerException.getLocalizedMessage());
}
return writer.toString();
}
public static Document getDocumentFromSoapRequestString(String xmlString) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
return doc;
}
private static void generateXsd(String operationName, String soapRequestXml) throws IOException, ParseException, ParserConfigurationException, SAXException, TransformerException {
FileWriter fw = new FileWriter("exmp.xml");
fw.write(soapRequestXml);
fw.close();
XsdGen gen = new XsdGen();
File tempFile = new File("exmp.xml");
gen.parse(tempFile);
File out = new File(operationName+".xsd");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
gen.write(new FileOutputStream(out));
Document doc = builder.parse(operationName+".xsd");
Element docElement = doc.getDocumentElement();
docElement.setAttribute("targetNamespace", "Default target name space for data mapper");
DOMSource source = new DOMSource(doc);
FileWriter writer = new FileWriter(new File(operationName+".xsd"));
StreamResult result = new StreamResult(writer);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.transform(source, result);
tempFile.delete();
}
}
I would like to know if is it good and safe approach to use the soupUI jar for the same?
Is there any alternative way to achieve the same?

Junit4 test implementation with parameters passed from XML, (attribute value + nodevalue)?

I know I can hardcode it (parse xml, extract), but is there a way to feed (attribute value + nodevalue) like it is done with Feed4TestNG (it currently support only csv, and excel files)?
I am new to Java, and any expert insight would be helpful. Thanks!
The body of a #Parameters is not limited to data only, you are able to use any java code you like in this method, including throwing exceptions:
#Parameters
public static Collection<Object[]> data() throws IOException {
List<Object[]> data = new ArrayList<>();
// replace getClass() with <nameofclass>.class
try(InputStream in = this.getClass().getResourceAsStream()) {
//parse body here
data.add(new Object[]{attribute, value});
}
return data;
}
Depending on what XML framework you use, you need to parse your XML nodes, and put it in the list, that you are going to return.
So this is what I end up doing here:
Please submit your correction if you think I can improve my code.
.
#RunWith(Parameterized.class)
public class DataDrivenTests {
private String c;
private String b;
private static Collection<Object[]> a;
#Parameters
public static Collection<Object[]> xmlData() throws IOException{
File file = new File("xmlfile.xml");
InputStream xml1 = new FileInputStream(file);
return new xmlData(xml1).getData();
}
public DataDrivenTests(String c, String b) {
super();
this.c = c;
this.b = b;
}
#Test
public void shouldCalculateATimesB() {
boolean assertion = false;
if(c.equals(Parser.parse("Parse this string to Attribute and Value"))){
assertion = true;
}
assertTrue(assertion);
}
}
xmlData.java
public class xmlData{
private transient Collection<Object[]> data = null;
public xmlData(final InputStream xml)throws IOException{
this.data = loadFromXml(xml);
}
public Collection<Object[]> getData(){
return data;
}
private Collection<Object[]> loadFromXml(final InputStream xml)
throws IOException {
List <Object[]> ism_code_map = new ArrayList<Object[]>();
try{
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setNamespaceAware(true);
DocumentBuilder dBuilder;
dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xml);
doc.getDocumentElement().normalize();
XPath xPath = XPathFactory.newInstance().newXPath();
XPathExpression expression = xPath.compile("//e");
NodeList nodes = (NodeList) expression.evaluate(doc, XPathConstants.NODESET);
for (int i =0; i< nodes.getLength(); i++){
Node nNode = nodes.item(i);
//System.out.println("\nCurrent Element:" + nNode.getTextContent());
if (nNode.getNodeType() == Node.ELEMENT_NODE){
Element eElement = (Element) nNode;
if(eElement.getAttribute("attrname") != null && !eElement.getAttribute("attrname").isEmpty()){
code_map.add(new Object[]{"attrname",eElement.getAttribute("attrname")});
}
}catch(ParserConfigurationException e){
e.printStackTrace();
}catch(SAXException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}catch(XPathExpressionException e){
e.printStackTrace();
}
catch(NullPointerException e){
e.printStackTrace();
}
return code_map;
}
}

DOMSource remains empty for String to XML

I am trying to generate XML file from String using DOMParser. The code snippet is like this:
public class SomeClassToSendString ()
{
private XMLBuilder xmlBuilder;
public SomeClassToSendString(String fileName) {
this.fileName = fileName;
xmlBuilder = new XMLBuilder(fileName)
}
public void writeHeader{
xmlBuilder.writeRootElements(); }
public void writeRecords(String record){
xmlBuilder.writeRecords(record); }
public void close{
xmlBuilder.close() }
}
public class XMLBuilder {
private String fileName;
private DocumentBuilderFactory docFactory;
private DocumentBuilder docBuilder;
private Document document;
private Element rootElement;
private Element childOfRoot;
public XMLFileHandler(String fileName) throws ParserConfigurationException {
this.fileName = fileName;
docFactory = DocumentBuilderFactory.newInstance();
docBuilder = docFactory.newDocumentBuilder();
document = docBuilder.newDocument();
}
public void writeRootElements() throws AppException{
try {
// root element
rootElement = document.createElement("root");
document.appendChild(rootElement);
// child element
childOfRoot = document.createElement("childOfRoot");
rootElement.appendChild(childOfRoot);
} catch (Exception e) {
logger.error(e);
}
}
public void writeRecords(String string) {
Element childOfFirstChild = document.createElement("childOfFirstChild");
childOfRoot.appendChild(childOfFirstChild);
Element record = document.createElement("record");
record.appendChild(document.createTextNode(string));
childOfFirstChild.appendChild(record); }
public void close() {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult xmlResultFile = new StreamResult(new File(fileName));
transformer.transform(source, xmlResultFile); }
}
The Document remains null so in turn the DOMSource is empty. I am unable to figure out where am I going off track.
Thanks.

Parse XML escaped in CDATA mixed with invalid HTML

I have the below element in a web service response. As you can see, it's escaped XML dumped as CDATA, so the XML parser just looks at it as a string and I'm unable to get the data I need from it through the usual means of XSLT and XPath. I need to turn this ugly string back into XML so that I can read it properly.
I have tried to do a search replace and simply converted all < to < and > to > and this works great, but there is a problem: The message.body element can actually contain HTML which is not valid XML. Might not even be valid HTML for all I know. So if I just replace everything, this will probably crash when I try to turn the string back into an XML document.
How can I unescape this safely? Is there a good way to do the replacement in the whole string except between the message.body open and closing tags for example?
<output><item type="object">
<ticket.id type="string">171</ticket.id>
<ticket.title type="string">SoapUI Test</ticket.title>
<ticket.created_at type="string">2013-12-03 12:50:54</ticket.created_at>
<ticket.status type="string">Open</ticket.status>
<updated type="string">false</updated>
<message type="object">
<message.id type="string">520</message.id>
<message.created_at type="string">2013-12-03 12:50:54.000</message.created_at>
<message.author type="string"/>
<message.body type="string">Just a test message...</message.body>
</message>
<message type="object">
<message.id type="string">521</message.id>
<message.created_at type="string">2013-12-03 13:58:32.000</message.created_at>
<message.author type="string"/>
<message.body type="string">Another message!</message.body>
</message>
</item>
</output>
This is actually lifted from the project i'm working on right now.
private Node stringToNode(String textContent) {
Element node = null;
try {
node = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new ByteArrayInputStream(textContent.getBytes()))
.getDocumentElement();
} catch (SAXException e) {
logger.error(e.getMessage(), e);
} catch (IOException e) {
logger.error(e.getMessage(), e);
} catch (ParserConfigurationException e) {
logger.error(e.getMessage(), e);
}
return node;
}
This will give you a document object representing the string. I use this to get this back into the original document:
if (textContent.contains(XML_HEADER)) {
textContent = textContent.substring(textContent.indexOf(XML_HEADER) + XML_HEADER.length());
}
Node newNode = stringToNode(textContent);
if (newNode != null) {
Node importedNode = soapBody.getOwnerDocument().importNode(newNode, true);
nextChild.setTextContent(null);
nextChild.appendChild(importedNode);
}
This is my current solution. You give it an XPath for the nodes that are messed up and a set of element names that might include messed up HTML and other problems. Works roughly as follows
Pull out text content of nodes matched by XPATH
Run regex to wrap problematic child elements in CDATA
Wrap text in temporary element (otherwise it crashes if there are multiple root nodes)
Parse text back to DOM
Add child nodes of temporary node back in place of previous text content.
The regex solution in step 2 is probably not fool-proof, but don't really see a better solution at the moment. If you do, let me know!
CDataFixer
import java.util.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
public class CDataFixer
{
private final XmlHelper xml = XmlHelper.getInstance();
public Document fix(Document document, String nodesToFix, Set<String> excludes) throws XPathExpressionException, XmlException
{
return fix(document, xml.newXPath().compile(nodesToFix), excludes);
}
private Document fix(Document document, XPathExpression nodesToFix, Set<String> excludes) throws XPathExpressionException, XmlException
{
Document wc = xml.copy(document);
NodeList nodes = (NodeList) nodesToFix.evaluate(wc, XPathConstants.NODESET);
int nodeCount = nodes.getLength();
for(int n=0; n<nodeCount; n++)
parse(nodes.item(n), excludes);
return wc;
}
private void parse(Node node, Set<String> excludes) throws XmlException
{
String text = node.getTextContent();
for(String exclude : excludes)
{
String regex = String.format("(?s)(<%1$s\\b[^>]*>)(.*?)(</%1$s>)", Pattern.quote(exclude));
text = text.replaceAll(regex, "$1<![CDATA[$2]]>$3");
}
String randomNode = "tmp_"+UUID.randomUUID().toString();
text = String.format("<%1$s>%2$s</%1$s>", randomNode, text);
NodeList parsed = xml
.parse(text)
.getFirstChild()
.getChildNodes();
node.setTextContent(null);
for(int n=0; n<parsed.getLength(); n++)
node.appendChild(node.getOwnerDocument().importNode(parsed.item(n), true));
}
}
XmlHelper
import java.io.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.sax.*;
import javax.xml.transform.stream.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
import org.xml.sax.*;
public final class XmlHelper
{
private static final XmlHelper instance = new XmlHelper();
public static XmlHelper getInstance()
{
return instance;
}
private final SAXTransformerFactory transformerFactory;
private final DocumentBuilderFactory documentBuilderFactory;
private final XPathFactory xpathFactory;
private XmlHelper()
{
documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
xpathFactory = XPathFactory.newInstance();
TransformerFactory tf = TransformerFactory.newInstance();
if (!tf.getFeature(SAXTransformerFactory.FEATURE))
throw new RuntimeException("Failed to create SAX-compatible TransformerFactory.");
transformerFactory = (SAXTransformerFactory) tf;
}
public DocumentBuilder newDocumentBuilder()
{
try
{
return documentBuilderFactory.newDocumentBuilder();
}
catch (ParserConfigurationException e)
{
throw new RuntimeException("Failed to create new "+DocumentBuilder.class, e);
}
}
public XPath newXPath()
{
return xpathFactory.newXPath();
}
public Transformer newIdentityTransformer(boolean omitXmlDeclaration, boolean indent)
{
try
{
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, omitXmlDeclaration ? "yes" : "no");
return transformer;
}
catch (TransformerConfigurationException e)
{
throw new RuntimeException("Failed to create Transformer instance: "+e.getMessage(), e);
}
}
public Templates newTemplates(String xslt) throws XmlException
{
try
{
return transformerFactory.newTemplates(new DOMSource(parse(xslt)));
}
catch (TransformerConfigurationException e)
{
throw new RuntimeException("Failed to create templates: "+e.getMessage(), e);
}
}
public Document parse(String xml) throws XmlException
{
return parse(new InputSource(new StringReader(xml)));
}
public Document parse(InputSource xml) throws XmlException
{
try
{
return newDocumentBuilder().parse(xml);
}
catch (SAXException e)
{
throw new XmlException("Failed to parse xml: "+e.getMessage(), e);
}
catch (IOException e)
{
throw new XmlException("Failed to read xml: "+e.getMessage(), e);
}
}
public String toString(Node node)
{
return toString(node, true, false);
}
public String toString(Node node, boolean omitXMLDeclaration, boolean indent)
{
try
{
StringWriter writer = new StringWriter();
newIdentityTransformer(omitXMLDeclaration, indent)
.transform(new DOMSource(node), new StreamResult(writer));
return writer.toString();
}
catch (TransformerException e)
{
throw new RuntimeException("Failed to transform XML into string: " + e.getMessage(), e);
}
}
public Document copy(Document document)
{
DOMSource source = new DOMSource(document);
DOMResult result = new DOMResult();
try
{
newIdentityTransformer(true, false)
.transform(source, result);
return (Document) result.getNode();
}
catch (TransformerException e)
{
throw new RuntimeException("Failed to copy XML: " + e.getMessage(), e);
}
}
}

Read escaped quote as escaped quote from xml

I load xml file into DOM model and analyze it.
The code for that is:
public class MyTest {
public static void main(String[] args) {
Document doc = XMLUtils.fileToDom("MyTest.xml");//Loads xml data to DOM
Element rootElement = doc.getDocumentElement();
NodeList nodes = rootElement.getChildNodes();
Node child1 = nodes.item(1);
Node child2 = nodes.item(3);
String str1 = child1.getTextContent();
String str2 = child2.getTextContent();
if(str1 != null){
System.out.println(str1.equals(str2));
}
System.out.println();
System.out.println(str1);
System.out.println(str2);
}
}
MyTest.xml
<tests>
<test name="1">ff1 "</test>
<test name="2">ff1 "</test>
</tests>
Result:
true
ff1 "
ff1 "
Desired result:
false
ff1 "
ff1 "
So I need to distinguish these two cases: when the quote is escaped and is not.
Please help.
Thank you in advance.
P.S. The code for XMLUtils#fileToDom(String filePath), a snippet from XMLUtils class:
static {
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
dFactory.setNamespaceAware(false);
dFactory.setValidating(false);
try {
docNonValidatingBuilder = dFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
}
}
public static DocumentBuilder getNonValidatingBuilder() {
return docNonValidatingBuilder;
}
public static Document fileToDom(String filePath) {
Document doc = getNonValidatingBuilder().newDocument();
File f = new File(filePath);
if(!f.exists())
return doc;
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
DOMResult result = new DOMResult(doc);
StreamSource source = new StreamSource(f);
transformer.transform(source, result);
} catch (Exception e) {
return doc;
}
return doc;
}
I've take a look on source code of apache xerces and propose my solution (but it is monkey patch).
I've wrote simple class
package a;
import java.io.IOException;
import org.apache.xerces.impl.XMLDocumentScannerImpl;
import org.apache.xerces.parsers.NonValidatingConfiguration;
import org.apache.xerces.xni.XMLString;
import org.apache.xerces.xni.XNIException;
import org.apache.xerces.xni.parser.XMLComponent;
public class MyConfig extends NonValidatingConfiguration {
private MyScanner myScanner;
#Override
#SuppressWarnings("unchecked")
protected void configurePipeline() {
if (myScanner == null) {
myScanner = new MyScanner();
addComponent((XMLComponent) myScanner);
}
super.fProperties.put(DOCUMENT_SCANNER, myScanner);
super.fScanner = myScanner;
super.fScanner.setDocumentHandler(this.fDocumentHandler);
super.fLastComponent = fScanner;
}
private static class MyScanner extends XMLDocumentScannerImpl {
#Override
protected void scanEntityReference() throws IOException, XNIException {
// name
String name = super.fEntityScanner.scanName();
if (name == null) {
reportFatalError("NameRequiredInReference", null);
return;
}
super.fDocumentHandler.characters(new XMLString(("&" + name + ";")
.toCharArray(), 0, name.length() + 2), null);
// end
if (!super.fEntityScanner.skipChar(';')) {
reportFatalError("SemicolonRequiredInReference",
new Object[] { name });
}
fMarkupDepth--;
}
}
}
You need to add only next line to your main method before start parsing
System.setProperty(
"org.apache.xerces.xni.parser.XMLParserConfiguration",
"a.MyConfig");
And you will have expected result:
false
ff1 "
ff1 "
Looks like you can get the TEXT_NODE child and use getNodeValue (assuming it's not NULL):
public static String getRawContent(Node n) {
if (n == null) {
return null;
}
Node n1 = getChild(n, Node.TEXT_NODE);
if (n1 == null) {
return null;
}
return n1.getNodeValue();
}
Grabbed that from:
http://www.java2s.com/Code/Java/XML/Gettherawtextcontentofanodeornullifthereisnotext.htm
There is no way to do this for the internal entities. XML does not support this concept. Internal entities are just a different way to write the same PSVI content into the text, they are not distinctive.

Categories