Work at SourceForge, help us to make it a better place! We have an immediate need for a Support Technician in our San Francisco or Denver office.

Close

Removal of optional elements

2009-04-06
2012-10-08
  • Alexander Keim
    Alexander Keim
    2009-04-06

    Hello,

    I'd like to run an XSLT that removes all elements from the input XML document which are optional according to the related schema. Is that possible? Is there a way to determine if an element ist mandatory or optional???

    Regards
    - Alex

     
    • Michael Kay
      Michael Kay
      2009-04-06

      Assuming a schema-aware transformation, you could try something like this:

      <xsl:template match="*[my:isOptional(.., node-name())]"/>

      where my:isOptional maps to a Java extension function whose logic is a method such as

      public static boolean isOptional(NodeInfo parent, QNameValue childName) {
      Configuration config = parent.getConfiguration();
      SchemaType parentType = config.getSchemaType(parent.getTypeAnnotation());
      int fp = config.getNamePool().allocate("", childName.getComponent(Component.NAMESPACE), childName.getComponent(Component.LOCALNAME));
      int cardinality = ((ComplexType)parentType).getElementParticleCardinality(fp);
      return Cardinality.allowsZero(cardinality);
      }

      Not tested, and there's probably some detail missing, but hopefully this is an avenue you could explore.

       
      • Alexander Keim
        Alexander Keim
        2009-04-06

        Thanks for the fast response. I'll give your suggestion a try and report on the outcome.

        Best regards
        - Alex

         
      • Alexander Keim
        Alexander Keim
        2009-04-07

        Got your code running. Just had to modify one line to get it compiled:

        int fp = config.getNamePool().allocate("", childName.getComponent(Component.NAMESPACE).toString(), childName.getComponent(Component.LOCALNAME).toString());

        Nevertheless the extension function always returns true and I'm not so skilled in the saxon libs to see the actual problem. If you could spare some of your valuable time and have a second look at code - that would really be great!!!

         
      • Michael Kay
        Michael Kay
        2009-04-07

        Are you sure you validated the input document?

        It works for me: working test case below. I had to use the match pattern //*[...] to avoid deleting top-level elements. I think that when you match the outermost element, .. will be a document node, and the extension function doesn't handle this case. At the next level down you typically find an element with minOccurs=0, maxOccurs=unbounded which counts as optional under this rule. So you may need to refine the rules.

        public void testExtensionWithSchemaAccecss() {
            // Create a Processor instance.
            try {
                Processor proc = new Processor(true);
                proc.setConfigurationProperty(&quot;http://saxon.sf.net/feature/timing&quot;, &quot;true&quot;);
                XsltCompiler comp = proc.newXsltCompiler();
                comp.setSchemaAware(true);
                StringReader sr = new StringReader(&quot;&lt;xsl:stylesheet version='2.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:my='java:s9apitest.TestXsltTransformer' exclude-result-prefixes='my'&gt;&quot; +
                        &quot;&lt;xsl:template match='*'&gt;&lt;xsl:copy&gt;&lt;xsl:copy-of select='@*'/&gt;&lt;xsl:apply-templates/&gt;&lt;/xsl:copy&gt;&lt;/xsl:template&gt;\n&quot; +
                        &quot;&lt;xsl:template match='*/*/*/*[my:isOptional(.., node-name(.))]'/&gt; \n&quot; +
                        &quot;&lt;/xsl:stylesheet&gt;&quot;);
                XsltExecutable exec = comp.compile(new StreamSource(sr));
                XsltTransformer transformer = exec.load();
        
                SchemaManager sm = proc.getSchemaManager();
                sm.load(new StreamSource(new File(&quot;c:/MyJava/testdata/books.xsd&quot;)));
                DocumentBuilder builder = proc.newDocumentBuilder();
                builder.setSchemaValidator(sm.newSchemaValidator());
                XdmNode books = builder.build(new File(&quot;c:/MyJava/testdata/books.xml&quot;));
                transformer.setInitialContextNode(books);
                // Create a serializer
                //String outfile = @&quot;C:/MyJava/users/caffo/Output.xml&quot;;
                StringWriter sw = new StringWriter();
                Serializer serializer = new Serializer();
                serializer.setOutputProperty(Serializer.Property.INDENT, &quot;yes&quot;);
                serializer.setOutputWriter(sw);
                transformer.setDestination(serializer);
                transformer.transform();
                assertTrue(sw.toString().contains(&quot;TITLE&quot;));
                assertFalse(sw.toString().contains(&quot;AUTHOR&quot;));
            } catch (SaxonApiException e) {
                e.printStackTrace();
                fail(e.getMessage());
            }
        
        }
        
        /**
         * Sample extension function - explores schema information
         */
        
        public static boolean isOptional(NodeInfo parent, QNameValue childName) throws Exception {
            Configuration config = parent.getConfiguration();
            SchemaType parentType = config.getSchemaType(parent.getTypeAnnotation());
            int fp = config.getNamePool().allocate(&quot;&quot;, childName.getComponent(Component.NAMESPACE).getStringValue(),
                    childName.getComponent(Component.LOCALNAME).getStringValue());
            int cardinality = ((ComplexType)parentType).getElementParticleCardinality(fp);
            return Cardinality.allowsZero(cardinality);
        }