Search code examples
javaxmlxsdxmlbeans

How to generate XML data from XSD using Java?


In an application I'm working on I need to generate sample data (XML instance) from XSD. I have XSD in the form of a String and need to generate respective XML as String again.

For example consider the below XSD

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Employee">
    <xs:complexType>
      <xs:sequence>
        <xs:element type="xs:string" name="name"/>
        <xs:element type="xs:byte" name="age"/>
        <xs:element type="xs:string" name="role"/>
        <xs:element type="xs:string" name="gender"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

I want to generate

<Employee>
  <name>string</name>
  <age>2</age>
  <role>string</role>
  <gender>string</gender>
</Employee>

After searching for a while came across various online tools which does this, but I want to be able to achieve it using Java. Also there are IDEs like Eclipse, Netbeans, IntelliJ which are able to achieve the desired functionality, except that they rely on XSD to be given as a File.

After bit of searching around seems like most of these use Apache XMLBeans.

I tried following the installation guide and setup all the environment variables mentioned like below

export XMLBEANS_HOME=/home/user/Programs/xmlbeans-3.1.0
PATH=$PATH:$XMLBEANS_HOME/bin
export CLASSPATH=$XMLBEANS_HOME/lib/xmlbeans-3.1.0.jar:$CLASSPATH
export XMLBEANS_LIB=/home/user/Programs/xmlbeans-3.1.0/lib/xmlbeans-3.1.0.jar

After all this if I run the command given below

./xsd2inst ../../Schema.xsd

I get error

Error: Could not find or load main class org.apache.xmlbeans.impl.xsd2inst.SchemaInstanceGenerator

Questions:

  1. What can I do to fix this error?
  2. If I get this working I can probably invoke the this command from a java process after writing XSD string in to a file and pass that as an argument to the command just like I have shown above. But I don't think this is an elegant solution, Is there any other way to accomplish what I have mentioned?

Note:

  1. I cannot make use of any commercial products/libraries.
  2. I'm aware of using JAXB, but that will require me to create a POJO for the types that I want to generate data for which isn't something that I can do since XSD data is dynamic and I cannot reuse those POJOs even if I create it.

Solution

  • After digging in a little bit, realised the environment variable value for XMLBEANS_LIB is wrongly set. The XMLBEANS_LIB expects to be pointed to the lib directory of the XML Beans distribution not to xmlbeans-3.1.0.jar. So the correct value for that is

     export XMLBEANS_LIB=/home/user/Programs/xmlbeans-3.1.0/lib
    

    I was able to generate the XMLInstance (as a String) using XSD ( given as String) using the below code.

    import org.apache.xmlbeans.SchemaType;
    import org.apache.xmlbeans.SchemaTypeSystem;
    import org.apache.xmlbeans.XmlBeans;
    import org.apache.xmlbeans.XmlException;
    import org.apache.xmlbeans.XmlObject;
    import org.apache.xmlbeans.XmlOptions;
    import org.apache.xmlbeans.impl.xsd2inst.SampleXmlUtil;
    import org.apache.xmlbeans.impl.xsd2inst.SchemaInstanceGenerator;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class XmlInstanceGeneratorImpl {
      private static final Logger logger = LogManager.getLogger(XmlInstanceGeneratorImpl.class);
    
      /**
       * Specifies if network downloads are enabled for imports and includes.
       * Default value is {@code false}
       */
      private static final boolean ENABLE_NETWORK_DOWNLOADS = false;
    
      /**
       * disable particle valid (restriction) rule
       * Default value is {@code false}
       */
      private static final boolean NO_PVR = false;
    
      /**
       * disable unique particle attribution rule.
       * Default value is {@code false}
       */
      private static final boolean NO_UPA = false;
    
    
      public String generateXmlInstance(String xsdAsString, String elementToGenerate){
        return generateXmlInstance(xsdAsString, elementToGenerate, ENABLE_NETWORK_DOWNLOADS, NO_PVR, NO_UPA);
      }
    
    
      public String generateXmlInstance(String xsAsString, String elementToGenerate, boolean enableDownloads,
                                        boolean noPvr, boolean noUpa){
        List<XmlObject> schemaXmlObjects = new ArrayList<>();
        try {
          schemaXmlObjects.add(XmlObject.Factory.parse(xsAsString));
        } catch (XmlException e) {
          logger.error("Error Occured while Parsing Schema",e);
        }
        XmlObject[] xmlObjects = schemaXmlObjects.toArray(new XmlObject[1]);
        SchemaInstanceGenerator.Xsd2InstOptions options = new SchemaInstanceGenerator.Xsd2InstOptions();
        options.setNetworkDownloads(enableDownloads);
        options.setNopvr(noPvr);
        options.setNoupa(noUpa);
        return xsd2inst(xmlObjects, elementToGenerate, options);
      }
    
      private String xsd2inst(XmlObject[] schemas, String rootName, SchemaInstanceGenerator.Xsd2InstOptions options){
        SchemaTypeSystem schemaTypeSystem = null;
        if (schemas.length > 0) {
          XmlOptions compileOptions = new XmlOptions();
          if (options.isNetworkDownloads())
            compileOptions.setCompileDownloadUrls();
          if (options.isNopvr())
            compileOptions.setCompileNoPvrRule();
          if (options.isNoupa())
            compileOptions.setCompileNoUpaRule();
          try {
            schemaTypeSystem = XmlBeans.compileXsd(schemas, XmlBeans.getBuiltinTypeSystem(), compileOptions);
          } catch (XmlException e) {
            logger.error("Error occurred while compiling XSD",e);
          }
        }
        if (schemaTypeSystem == null) {
          throw new RuntimeException("No Schemas to process.");
        }
    
        SchemaType[] globalElements = schemaTypeSystem.documentTypes();
        SchemaType elem = null;
        for (SchemaType globalElement : globalElements) {
          if (rootName.equals(globalElement.getDocumentElementName().getLocalPart())) {
            elem = globalElement;
            break;
          }
        }
        if (elem == null) {
          throw new RuntimeException("Could not find a global element with name \"" + rootName + "\"");
        }
        // Now generate it and return the result
        return SampleXmlUtil.createSampleForType(elem);
      }
    }