4

i've got this servicedefinition:

[DataContract]
public class Test
{

    [DataMember(IsRequired = true)]
    public TestArray[] array;
}

[DataContract]
public class TestArray
{
   public DateTime? field1;
   public string field2;
}

which WCFs Metadataprovider ( http://localhost/Test?wsdl ) generates as:

<xs:complexType name="ArrayOfTestArray">
  <xs:sequence>
  <xs:element minOccurs="0" maxOccurs="unbounded" name="array" nillable="true" type="tns:TestArray"/>
  </xs:sequence>
</xs:complexType>

<xs:complexType name="OpenBalanceInvoice">
  <xs:sequence>
    <xs:element name="field1" nillable="true" type="xs:dateTime"/>
    <xs:element name="field2" nillable="true" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

The problem is ( even if it works when svcutil.exe magically generates a client from it ) that the Metadataprovider actually creates a new object ( ArrayOfTestArray ) which is not existent in the code it was generated from

Problem: When i try to generate a JavaClient from this WSDL, it - of course - doesn't recognize that this "ArrayOf" object isn't a "real" object at all and the Java class looks something like:

class Test
{
   public ArrayOfTestArray array;
}

class ArrayOfTestArray 
{
   public TestArray[] array;
}
public class TestArray
{
   public DateTime? field1;
   public string field2;
}

So, i don't want this additional class of course... any suggestions?

Thanks!

David
  • 2,551
  • 3
  • 34
  • 62
  • Not sure if this is helpful, but I'm currently consuming a J2EE web service and I see lots of the ArrayOfXXXX classes in the wsdl. Maybe it's the standard way to do that?? Not sure. – Tad Donaghe Feb 08 '10 at 15:53
  • I'm not a .NET person so I'm not entirely sure how the code is generated in .NET but it looks like the reason you have the 'ArrayOfTestArray' is becuase you have 'TestArray' declared as required and the code generator is generating exactly what you have asked it to. The Java classes are generated from the wsdl which does have that type defined so the classes appear to be generated correctly. I did not post as an answer because I'm really unsure how .NET generates code for web services. – ChadNC Feb 08 '10 at 15:56
  • When asking some of my collegues it came out the the ArrayOf construct is standard behavior... well, no idea on the issue on the java side so far – David Feb 08 '10 at 17:07

1 Answers1

1

My suggestion is to massage the XSD file itself, since there's probably not much you can do on the .NET side of things.

The following XSLT should work in your case (provided the element and the complex types are in the same schema file):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <xsl:template match="xsd:complexType[@name]">
        <!-- This templace filters out any complexTypes with a name starting with 'ArrayOf' -->
        <xsl:if test="not(starts-with(@name, 'ArrayOf'))">
            <xsl:copy>
                <xsl:apply-templates select="@*" />
                <xsl:apply-templates />
            </xsl:copy>
        </xsl:if>
    </xsl:template>

    <xsl:template match="xsd:element[@type]">
        <!-- This templace inlines any elements referencing a type starting with 'ArrayOf' -->
        <xsl:variable name="typeName">
            <xsl:choose>
                <xsl:when test="contains(@type, ':')">
                    <xsl:value-of select="substring-after(@type, ':')" />
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="@type" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:choose><!-- Should we inline? -->
            <xsl:when test="starts-with($typeName, 'ArrayOf')">
                <!-- Yep, inline it, but wrap in a sequence! -->
                <xsl:apply-templates select="xsd:annotation" />
                <xsd:element>
                    <!-- copy over attributes such as name, minOccurs, maxOccurs, nillable -->
                    <xsl:copy-of select="@*[local-name(.) != 'type']" />
                    <xsl:comment>
                        Inlined from <xsl:value-of select="@type" />):
                    </xsl:comment>
                    <xsd:complexType>
                        <xsl:apply-templates select="//xsd:complexType[@name=$typeName]/*" />
                    </xsd:complexType>
                    <xsl:comment>End of inlined element</xsl:comment>
                </xsd:element>

                <xsl:apply-templates
                    select="xsd:attribute | xsd:attributeGroup | xsd:attributeGroup" />
            </xsl:when>
            <xsl:otherwise>
                <!-- Nah, just copy -->
                <xsl:copy>
                    <xsl:apply-templates select="@*" />
                    <xsl:apply-templates />
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

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

Given this input XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.example.org/ExtensionFlattener" xmlns:tns="http://www.example.org/ExtensionFlattener"
    elementFormDefault="qualified">

    <xs:element name="SomeElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="First" type="xs:string" />
                <xs:element name="Second" minOccurs="0" type="tns:ArrayOfTestArray" nillable="true"/>
                <xs:element name="Third" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="ArrayOfTestArray">
        <xs:sequence>
            <xs:element minOccurs="0" maxOccurs="unbounded" name="array"
                nillable="true" type="tns:TestArray" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="TestArray">
        <xs:sequence>
            <xs:element name="field1" nillable="true" type="xs:dateTime" />
            <xs:element name="field2" nillable="true" type="xs:string" />
        </xs:sequence>
    </xs:complexType>

</xs:schema>

it will turn it into:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://www.example.org/ExtensionFlattener" targetNamespace="http://www.example.org/ExtensionFlattener"
    elementFormDefault="qualified">

    <xs:element name="SomeElement">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="First" type="xs:string" />
                <xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                    name="Second" minOccurs="0" nillable="true">
                    <!-- Inlined from tns:ArrayOfTestArray): -->
                    <xsd:complexType>
                        <xs:sequence>
                            <xs:element minOccurs="0" maxOccurs="unbounded" name="array"
                                nillable="true" type="tns:TestArray" />
                        </xs:sequence>
                    </xsd:complexType>
                    <!--End of inlined element -->
                </xsd:element>
                <xs:element name="Third" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>



    <xs:complexType name="TestArray">
        <xs:sequence>
            <xs:element name="field1" nillable="true" type="xs:dateTime" />
            <xs:element name="field2" nillable="true" type="xs:string" />
        </xs:sequence>
    </xs:complexType>

</xs:schema>

This produces nicer Java classes (although it still gives you an extra, local, type):

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "first",
    "second",
    "third"
})
@XmlRootElement(name = "SomeElement")
public class SomeElement {

    @XmlElement(name = "First", required = true)
    protected String first;
    @XmlElementRef(name = "Second", namespace = "http://www.example.org/ExtensionFlattener", type = JAXBElement.class)
    protected JAXBElement<SomeElement.Second> second;
    @XmlElement(name = "Third", required = true)
    protected String third;

    // Plus getters and setters

    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "", propOrder = {
        "array"
    })
    public static class Second {

        @XmlElement(nillable = true)
        protected List<TestArray> array;

        // plus getter
    }
}

(plus the other class)

I hope this applies to your problem (pun intended!)

JesperSM
  • 1,418
  • 1
  • 11
  • 15