Reputation: 687
I have a XSD as shown below , i need to extract all the root Elements in the XSD and create a separate XSD for each root element pragmatically in java, is there some framework of java library that can aid me in achieving this.
<?xml version='1.0' encoding='windows-1252'?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org" targetNamespace="http://www.example.org" elementFormDefault="qualified">
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="HouseNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="OrderType">
<xsd:sequence>
<xsd:element name="orderID" type="xsd:string"/>
<xsd:element name="billTo" type="USAddress"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string"/>
<xsd:element name="Address" type="USAddress"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="Order" type="OrderType"/>
</xsd:schema>
TO
<?xml version='1.0' encoding='windows-1252'?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org" targetNamespace="http://www.example.org" elementFormDefault="qualified">
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="HouseNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="OrderType">
<xsd:sequence>
<xsd:element name="orderID" type="xsd:string"/>
<xsd:element name="billTo" type="USAddress"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Order" type="OrderType"/>
</xsd:schema>
<?xml version='1.0' encoding='windows-1252'?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org" targetNamespace="http://www.example.org" elementFormDefault="qualified">
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="HouseNumber" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="Customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string"/>
<xsd:element name="Address" type="USAddress"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Upvotes: 0
Views: 722
Reputation: 6016
Using XSL 2.0 you can have multiple output documents and can define every file name, file content, etc. Default java support for XSL 2.0 is far from perfect, so I use the incredible Saxon (you can download saxon-he here, unzip it and add saxon9he.jar to your project).
This is the example XSL 2.0 I've made, it works using the idea that user laune has exposed in his comment. So it produces one output schema for every element, and a schema for everything that is not an element (types). Every new schema contains only a xs:element's and has a xs:include including the schema containing types. It is a quite simple (and very clever in my opinion) method and easy to understand if you're familiar with XSL (look at the xsl comments).
xsl.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:for-each select="/*/*[local-name()='element']">
<xsl:variable name="currentElement" select="."/>
<!-- Create schema for every element containing only element, including types schema -->
<xsl:result-document method="xml" href="schema_element_{@name}.xsd">
<xsl:for-each select="/*[1]">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:element name="include" namespace="http://www.w3.org/2001/XMLSchema">
<xsl:attribute name="schemaLocation">schema_types.xsd</xsl:attribute>
</xsl:element> <!-- Include clause -->
<xsl:copy-of select="$currentElement"/> <!-- Only content is current xs:element -->
</xsl:copy>
</xsl:for-each>
</xsl:result-document>
</xsl:for-each>
<xsl:for-each select="/*[1]">
<!-- Create types document containing all but root xs:element's -->
<xsl:result-document method="xml" href="schema_types.xsd">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:copy-of select="node()[local-name()!='element']"/>
</xsl:copy>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The only java code needed is for calling saxon to do the xsl transformation that produce the output files with the names dynamically derived in xsl.
Main.java
import java.io.File;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class Main {
public static void main(String[]args) throws Exception{
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource(new File("xsl.xsl")));
transformer.transform(new StreamSource(new File("schema.xsd")), new StreamResult(System.out));
}
}
Using your example schema, my xsl and running the java code these are the files that will be created with these filenames (note that indentation might be lost):
schema_element_Customer.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.org" targetNamespace="http://www.example.org"
elementFormDefault="qualified">
<include xmlns="http://www.w3.org/2001/XMLSchema"
schemaLocation="schema_types.xsd" />
<xsd:element name="Customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="CustomerID" type="xsd:string" />
<xsd:element name="Address" type="USAddress" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
schema_element_Order.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.org" targetNamespace="http://www.example.org"
elementFormDefault="qualified">
<include xmlns="http://www.w3.org/2001/XMLSchema"
schemaLocation="schema_types.xsd" />
<xsd:element name="Order" type="OrderType" />
</xsd:schema>
schema_types.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.example.org" targetNamespace="http://www.example.org"
elementFormDefault="qualified">
<xsd:complexType name="USAddress">
<xsd:sequence>
<xsd:element name="HouseNumber" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="OrderType">
<xsd:sequence>
<xsd:element name="orderID" type="xsd:string" />
<xsd:element name="billTo" type="USAddress" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Necessary changes to output to Map instead of files in the file-system:
Based on this answer I realized that you can add a setting to be notified by every result-document so you can writte the output where you want implementing an OutputUriResolver. So I've made this one that writes to a HashMap:
import java.io.StringWriter;
import java.util.Map;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import net.sf.saxon.lib.OutputURIResolver;
public class HashMapSaverOutputUriResolver implements OutputURIResolver{
private Map<String, String> results; // We save here
public HashMapSaverOutputUriResolver(Map<String, String> map){
this.results = map; // Set map
}
public OutputURIResolver newInstance() {
return this;
}
public Result resolve(String href, String base) throws TransformerException { // We set to resolve the file to write to a stringResult that wraps a stringWritter
StringWriter sw = new StringWriter(); // StringWritter
StreamResult sr = new StreamResult(sw); // StreamResult
sr.setSystemId(href); // Id of the streamResult = href = fileName
return sr;
}
public void close(Result result) throws TransformerException { // End of result:document transformed
StreamResult sr = (StreamResult) result; // Get StreamResult
StringWriter sw = (StringWriter) sr.getWriter(); // Get StreamWritter
String href = sr.getSystemId(); // Get href (fileName)
String content = sw.toString(); // Get string file-content
this.results.put(href, content); // Save in map
}
}
In the main file you only need to add two new lines (create the map, and set the proper setting to the OutputUriResolver associated with the map).
public static void main(String[] args) throws Exception {
HashMap<String, String> results = new HashMap<String, String>(); // Your map
TransformerFactory tFactory = TransformerFactory.newInstance();
tFactory.setAttribute("http://saxon.sf.net/feature/outputURIResolver", new HashMapSaverOutputUriResolver(results)); // Set OutputURIResolver
Transformer transformer = tFactory.newTransformer(new StreamSource(new File("xsl.xsl")));
transformer.transform(new StreamSource(new File("schema.xsd")), new StreamResult(System.out));
// Once the transformation has finished the Map is filled
}
Upvotes: 2