Question

I need to validate the following XML against a (complex) type declared in the following XSD, using XmlBeans.

My XSD:

<xs:schema targetNamespace="http://www.xxx.com/xmlns/osb/WS2CICS/program/PROG1/p1" attributeFormDefault="unqualified" elementFormDefault="qualified"
  xmlns="http://www.xxx.com/xmlns/osb/WS2CICS/program/PROG1/p1"
  xmlns:ct="http://www.xxx.com/xmlns/osb/WS2CICS/common"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <!--xs:element name="Param" type="ParamType"/-->

    <xs:complexType name="ParamType">
        <xs:sequence>
            <xs:element name="Text" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

My XML:

<Param xsi:type="p1:ParamType"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://www.xxx.com/xmlns/osb/WS2CICS/Envelope/v01"
  xmlns:p1="http://www.xxx.com/xmlns/osb/WS2CICS/program/PROG1/p1">

    <p1:Text>abc</p1:Text>

</Param>

I essentially use the code from this site for XML validation using XmlBeans. But the validation fails with the message "Invalid type" for the element "Param". The validation succeeds only after uncommenting the "Param"-element declaration in the XSD and after changing the namespace of "Param" in the XML by adding the namespace prefix p1: to it - that is when validating against an element declaration, not a type declaration.

What I need is:

  1. Use XmlBeans
  2. Tell somehow to XmlBeans that the element "{http://www.xxx.com/xmlns/osb/WS2CICS/Envelope/v01}Param" in the XML root is OK (no need to check).
  3. Tell XmlBeans that the root is of complex type "{http://www.xxx.com/xmlns/osb/WS2CICS/program/PROG1/p1}ParamType" (to check the children)

How am I to do that?

Background:

I have a message envelope where the "Param" element is of type "anyType", i.e. it can have any content. In a particular situation I need to check that "Param" has a particular content given by a dynamically chosen XSD. I can easily check the validity of the envelope, but in the second step, I need to check the validity of the Param too.

Edit: re-phrased

Was it helpful?

Solution 2

I found no direct way on how to validate an element against a XSD type, using XmlBeans. But I found a workaround by dynamically creating a schema for the missing element declaration:

/**
 * Validates an XML element (it needs not be the document root) against a type declared in an XSD-schema.
 * 
 * @param xmlObject XML element being validated
 * @param schemas compiled XML schema(s) used for the validation
 * @param xmlObjectType Qualified name of the schema type in <code>schemas</code> against which the element validation is performed
 * @throws XmlException thrown in case of a parsing or validation error
 */
public static void validate(XmlObject xmlObject, SchemaTypeSystem schemas, QName xmlObjectType) throws XmlException {
    Node node = xmlObject.getDomNode();
    //..... some argument checking here .....

    String elemName = node.getLocalName();
    String elemNamespace = node.getNamespaceURI();
    String typeName = xmlObjectType.getLocalPart();
    String typeNamespace = xmlObjectType.getNamespaceURI();
    String schemasKey = elemName+"@"+elemNamespace +"," +typeName+"@"+typeNamespace;  //perhaps it's sufficient

    // Prepare schema
    SchemaTypeSystem loader = schemas;
    SchemaGlobalElement schemaElem = loader.findElement(new QName(elemNamespace, elemName));
    if (schemaElem != null) {
        QName elemQName = schemaElem.getType().getName();
        if (!elemQName.getLocalPart().equals(typeName) || !elemQName.getNamespaceURI().equals(typeNamespace))
            throw new IllegalArgumentException("Requested type " +typeName+"@"+typeNamespace
                    +" of xmlObject is different from its actual type " +elemQName.getLocalPart()+"@"+elemQName.getNamespaceURI() +" in provided schema(s)");
    }
    else {
        //the schema does not contain the xmlObject element declaration => add it (artificially)
        String helperSchema = ""
                + "<xs:schema attributeFormDefault=\"unqualified\" elementFormDefault=\"qualified\"\n"
                + "  targetNamespace=\"" +elemNamespace +"\"\n"
                + "  xmlns=\"" +elemNamespace +"\"\n"
                + "  xmlns:t=\"" +typeNamespace +"\"\n"
                + "  xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">\n"
                + "\n"
                + "    <xs:element name=\"" +elemName +"\" type=\"t:" +typeName +"\"/>\n"
                + "\n"
                + "</xs:schema>\n";
        loader = XmlBeans.compileXsd(schemas,  // extend the schema
                new XmlObject[]{
                        XmlObject.Factory.parse(helperSchema, new XmlOptions().setLoadLineNumbers().setLoadMessageDigest()
                                .setDocumentSourceName(schemasKey))
                }, null,
                new XmlOptions().setErrorListener(null).setCompileDownloadUrls().setCompileNoPvrRule());
    }

    // validate the element using the loader in a standard way
    // (see the link above, for instance)
    validate(xmlObject, loader);
}

I receive an XmlObject on the input, but one would usually use a File object instead. In the implementation, resorting to DOM may not be optimal, though - if I new XmlBeans better, the code could be improved.

OTHER TIPS

You need to remember that an XSD schema file can only have one single target namespace. What you are trying to do here is to define a complexType 'ParamType' in the namespace http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1 and element 'Param' in the namespace http://www.example.com/xmlns/osb/WS2CICS/Envelope/v01. You should use two xsd schemas for this(import the other one) and change your XMLValidator class to take additional xsd file input like below -

input.xsd -

<xs:schema targetNamespace="http://www.example.com/xmlns/osb/WS2CICS/Envelope/v01"                         attributeFormDefault="unqualified" elementFormDefault="qualified"
 xmlns:p1="http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1"
 xmlns="http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1"
 xmlns:ct="http://www.example.com/xmlns/osb/WS2CICS/common"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xs:import namespace="http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1                      root.xsd"/>

 <xs:element name="Param" type="ParamType"/> 

</xs:schema>

root.xsd -

 <xs:schema targetNamespace="http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1" attributeFormDefault="unqualified" elementFormDefault="qualified"
 xmlns="http://www.example.com/xmlns/osb/WS2CICS/program/PROG1/p1"
 xmlns:ct="http://www.example.com/xmlns/osb/WS2CICS/common"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:complexType name="ParamType">
    <xs:sequence>
        <xs:element name="Text" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

XMLValidator -

 ...
 public boolean validate(File dataFile, File schemaFile, File schemaFile2) {
 ...

 ...
 XmlObject[] schemas = { XmlObject.Factory.parse(schemaFile,
                new XmlOptions().setLoadLineNumbers()
                        .setLoadMessageDigest()), XmlObject.Factory.parse(schemaFile2,
                                new XmlOptions().setLoadLineNumbers()
                                .setLoadMessageDigest()) };
 ...

When importing root.xsd inside input.xsd, you need to correctly point root.xsd. If you find problems, you may use absolute path of root.xsd.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top