Afonso Oliveira
Afonso Oliveira

Reputation: 11

Error in Generating an Java POJO from XML Schema that Contains Imports

I am working on a project where I need to automatically generate code for a POJO-like object from an XML Schema (XSD) file. To achieve this, I have created a class that takes the file path of the XSD as input and generates the corresponding code as a string.

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.xml.sax.InputSource;

import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.CodeWriter;
import com.sun.tools.xjc.api.ClassNameAllocator;
import com.sun.tools.xjc.api.S2JJAXBModel;
import com.sun.tools.xjc.api.SchemaCompiler;
import com.sun.tools.xjc.api.XJC;
import com.sun.codemodel.writer.SingleStreamCodeWriter;

public class SemanticPOJOCreator {

    public static String fromXSD(String xsdFilePath, String outClassName) throws IOException {
        SchemaCompiler sc = XJC.createSchemaCompiler();
        sc.forcePackageName("");

        sc.setClassNameAllocator(new ClassNameAllocator() {
            public String assignClassName(String packageName, String className) {
                return outClassName;
            }
        });
        
        File schemaFile = new File(xsdFilePath);
        try (FileInputStream fis = new FileInputStream(schemaFile)) {
            InputSource is = new InputSource(fis);
            URI uri = schemaFile.toURI();
            is.setSystemId(uri.toASCIIString());

            sc.parseSchema(is);
            S2JJAXBModel model = sc.bind(); // Here
            JCodeModel jCodeModel = model.generateCode(null, null);
            
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            CodeWriter codeWriter = new SingleStreamCodeWriter(baos);

            jCodeModel.build(codeWriter);

            String generatedCode = baos.toString();
            
            generatedCode = Arrays.stream(generatedCode.split("\n"))
                    .map(line -> {
                        if (line.startsWith("public class")) {
                            return line.replace("public class", "class");
                        } else {
                            return line;
                        }
                    })
                    .filter(line -> !line.startsWith("import") && !line.contains("-----"))
                    .collect(Collectors.joining("\n"));
            
            generatedCode = generatedCode.replaceAll("ObjectFactory", "ObjectFactory_" + outClassName);   
            
            return generatedCode;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}

The class works correctly with most XSD files. However, it triggers an error when the file contains an <xs:import> element. Below is an example of the XSD file causing the issue:

<?xml version="1.0" encoding="UTF-8"?>

<xs:schema 
      attributeFormDefault="unqualified" 
      elementFormDefault="qualified" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      xmlns:a3st ="http://gres.uninova.pt/a3st" 
      xmlns:sawsdl="http://www.w3.org/ns/sawsdl"
      xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"
      xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
      xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
      xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2">
   
<xs:import namespace="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"
schemaLocation="../common/UBL-CommonAggregateComponents-2.1.xsd"/>
<xs:import namespace="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"
schemaLocation="../common/UBL-CommonBasicComponents-2.1.xsd"/>
<xs:import namespace="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"
schemaLocation="../common/UBL-CommonExtensionComponents-2.1.xsd"/>

   <xs:complexType name="InvoiceType">
      <xs:sequence>
         <xs:element ref="cac:AllowanceCharge"  sawsdl:modelReference ="/TemperatureSensor{2}/hasDecValue">         
         </xs:element>      
         <xs:element ref="cac:TaxTotal">
         </xs:element>   
      </xs:sequence>
   </xs:complexType>
</xs:schema>

The error encountered is consistentently Exception in thread "main" java.lang.IllegalArgumentException: the root package cannot be annotated and occurs at S2JJAXBModel model = sc.bind();.

If the sc.forcePackageName(""); line is omitted, a different error occurs:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.sun.tools.xjc.api.S2JJAXBModel.generateCode(com.sun.tools.xjc.Plugin[], com.sun.tools.xjc.api.ErrorListener)" because "model" is null

Is the issue I'm encountering a limitation of the libraries or packages I am using, or could it be due to incorrect usage or configuration on my part?

Upvotes: 1

Views: 41

Answers (0)

Related Questions