Egor4eg
Egor4eg

Reputation: 2708

Combine XML and XSD files

I have following XML file:

<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38">
 <my:Text1></my:Text1>
 <my:Group>
  <my:Text2></my:Text2>
 </my:Group>
</my:myFields>

And XSD definition for it:

<xsd:schema targetNamespace="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <xsd:element name="myFields">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element ref="my:Text1" minOccurs="0"/>
    <xsd:element ref="my:Group" minOccurs="0" maxOccurs="unbounded" />
   </xsd:sequence>
   <xsd:anyAttribute processContents="lax" namespace="http://www.w3.org/XML/1998/namespace"/>
  </xsd:complexType>
 </xsd:element>
 <xsd:element name="Text1" type="xsd:string"/>
 <xsd:element name="Group">
  <xsd:complexType>
   <xsd:sequence>
    <xsd:element ref="my:Text2" minOccurs="0"/>
   </xsd:sequence>
  </xsd:complexType>
  <xsd:element name="Text2" type="xsd:string"/>
 </xsd:element>
</xsd:schema>

I would like to create following XML file which is based on the XML and XSD:

<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38">
 <my:Text1 type="string" minOccurs="0"></my:Text1>
 <my:Group minOccurs="0" maxOccurs="unbounded">
  <my:Text2 type="string" minOccurs="0"></my:Text2>
 </my:Group>
</my:myFields>

What is the easiest way to do that with .NET platform? Is it possible using XSLT transformation?

Upvotes: 0

Views: 2319

Answers (3)

Michael Kay
Michael Kay

Reputation: 163625

When you validate an XML instance document against a schema, the result is a PSVI (post-schema-validation instance) which contains all the information you need, and more. The only question is, how to get hold of this information in practice. This depends on the tools you want to use. If you use Xerces as your schema validator, for example, there is an API to expose the PSVI annotations added to the instance by the schema processor.

Upvotes: 0

Sean B. Durkin
Sean B. Durkin

Reputation: 12729

This XSLT 2.0 style-sheet will explore your schema an insert appropriate attribute values for minOccurs, maxOccurs an type. As it is modular based, it is easy to extend for other xsd attributes that you might like to include. For ease of demonstration I have inserte the schema inside the stylesheet. Although in prouction you are likely to leave it external. So ajust acordingly.

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38"
  xmlns:l="http://stackoverflow.com/questions/11502608"
  exclude-result-prefixes="xsl xs fn xsd l">
<xsl:output indent="yes"/>

 <xsd:schema targetNamespace="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38"

 ... etc .. INSERT SCHEMA HERE !!!

 </xsd:schema>


<xsl:template match="@*|node()">
 <xsl:copy>
  <xsl:apply-templates select="@*|node()" />
 </xsl:copy>  
</xsl:template>

<xsl:function name="l:xsd-attrib" as="attribute()?">
 <xsl:param name="ele-name" as="xs:string"/>
 <xsl:param name="attri-name" as="xs:string"/>
 <xsl:sequence select="document('')/xsl:stylesheet/xsd:schema
    //(xsd:element[ (substring-after(@ref,'my:')=$ele-name) or (@name=$ele-name)]
    /@*[local-name(self::attribute())=$attri-name])" />
</xsl:function>

<xsl:template name="l:emit-xsd-attri">
 <xsl:param name="my-node" as="element()" />
 <xsl:param name="attri-name" as="xs:string" />  
  <xsl:variable name="ele" select="local-name( $my-node)" />
  <xsl:if test="l:xsd-attrib( $ele, $attri-name)">
   <xsl:attribute name="{$attri-name}"><xsl:value-of select="
     for $l in l:xsd-attrib( $ele, $attri-name) return
      if (contains( $l, ':')) then substring-after( $l, ':') else $l" />
    </xsl:attribute>
  </xsl:if>
</xsl:template> 

<xsl:template match="my:*"> 
 <xsl:copy>
  <xsl:apply-templates select="@*" />

  <xsl:call-template name="l:emit-xsd-attri">
   <xsl:with-param name="my-node" select="." />
   <xsl:with-param name="attri-name" select="'minOccurs'" />  
  </xsl:call-template>

  <xsl:call-template name="l:emit-xsd-attri">
   <xsl:with-param name="my-node" select="." />
   <xsl:with-param name="attri-name" select="'maxOccurs'" />  
  </xsl:call-template>

  <xsl:call-template name="l:emit-xsd-attri">
   <xsl:with-param name="my-node" select="." />
   <xsl:with-param name="attri-name" select="'type'" />  
  </xsl:call-template>

  <xsl:apply-templates select="node()" />
 </xsl:copy>  
</xsl:template>

</xsl:stylesheet>

Upvotes: 2

Woody
Woody

Reputation: 5130

Given a corrected schema of

<xsd:schema targetNamespace="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="myFields">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="my:Text1" minOccurs="0"/>
                <xsd:element ref="my:Group" minOccurs="0" maxOccurs="unbounded" />
            </xsd:sequence>
            <xsd:anyAttribute processContents="lax" namespace="http://www.w3.org/XML/1998/namespace"/>
        </xsd:complexType>
    </xsd:element>
    <xsd:element name="Text1" type="xsd:string"/>
    <xsd:element name="Text2" type="xsd:string"/>
    <xsd:element name="Group">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element ref="my:Text2" minOccurs="0"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

and the XML input that you specified, this stylesheet

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:ext="http://exslt.org/common" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38">
<xsl:output method="xml" indent="yes"/>
    <xsl:variable name="schema" select="document('so.xsd')//xsd:schema"/>

<xsl:template match="/">
     <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
</xsl:template>

<xsl:template match="my:*">
    <xsl:variable name="name" select="name()"></xsl:variable>
    <xsl:variable name="basename" select="substring-after($name,':')"></xsl:variable>
    <xsl:copy>
        <xsl:variable name="types" select="$schema//xsd:element[@name=$basename]/@type"/>
        <xsl:if test="$types">
            <xsl:attribute name="type">
                <xsl:value-of select="$types"/>
            </xsl:attribute>
        </xsl:if>
        <xsl:for-each select="$schema//xsd:element[@ref=$name]/@*[name()!='ref']">
            <xsl:attribute name="{name()}">
                <xsl:value-of select="."/>
            </xsl:attribute>
        </xsl:for-each>
        <xsl:apply-templates select="*"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="node()|@*">
    <xsl:copy><xsl:apply-templates select="node()|@*" /></xsl:copy>
</xsl:template>

</xsl:stylesheet>

produces the following output, which I believe is what you wanted.

<?xml version="1.0" encoding="utf-8"?>
<my:myFields xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2012-05-05T12:20:38" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xd="http://schemas.microsoft.com/office/infopath/2003">
   <my:Text1 type="xsd:string" minOccurs="0"/>
   <my:Group minOccurs="0" maxOccurs="unbounded">
      <my:Text2 type="xsd:string" minOccurs="0"/>
   </my:Group>
</my:myFields>

What it does is load the schema as a document, then for each element, look up the document for an element of that type, copy the attribute on it, then look for a ref of the right type and copy those elements too.

The only problems is that it doesn't actualy travel down the schema looking for the right sections, it just addresses them by name. For small schemas, and probably most of the time it will be right, but occasionally it may cause an issue. But for what you are showing at the moment it will work, and it is a place to start from

Upvotes: 2

Related Questions