Skip to content

Populate xsi:type attribute on toXML(...)#518

Merged
eed3si9n merged 2 commits intoeed3si9n:masterfrom
nezasa:type_attribute
Oct 29, 2019
Merged

Populate xsi:type attribute on toXML(...)#518
eed3si9n merged 2 commits intoeed3si9n:masterfrom
nezasa:type_attribute

Conversation

@abestel
Copy link
Copy Markdown

@abestel abestel commented Oct 29, 2019

Context
I have an example here that reproduces the problem: https://github.com/abestel/scalaxb-failure/tree/bug/type_attribute

Basically, the WSDL defines types as:

    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ser="http://schemas.microsoft.com/2003/10/Serialization/" elementFormDefault="unqualified">
            <xs:complexType name="ServiceRequest">
                <xs:sequence>
                    <xs:element name="IP" nillable="true" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
            <xs:element name="ServiceRequest" nillable="true" type="ServiceRequest"/>

            <xs:complexType name="ServiceRequestResponse">
                <xs:sequence>
                    <xs:element name="IP" nillable="true" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
            <xs:element name="ServiceRequestResponse" nillable="true" type="ServiceRequestResponse"/>

            <xs:complexType name="MyRequest">
                <xs:complexContent mixed="false">
                    <xs:extension base="ServiceRequest">
                        <xs:sequence>
                            <xs:element name="ID" type="xs:int"/>
                        </xs:sequence>
                    </xs:extension>
                </xs:complexContent>
            </xs:complexType>
            <xs:element name="MyRequest" nillable="true" type="MyRequest"/>

            <xs:complexType name="MySubRequest">
                <xs:complexContent mixed="false">
                    <xs:extension base="MyRequest">
                        <xs:sequence/>
                    </xs:extension>
                </xs:complexContent>
            </xs:complexType>
            <xs:element name="MySubRequest" nillable="true" type="MySubRequest"/>
        </xs:schema>
    </wsdl:types>

So I have ServiceRequest <- MyRequest <- MySubRequest.
The inheritance is expressed as

  • MyRequestable = MyRequest + MySubRequest
  • ServiceRequestable = ServiceRequest + MyRequestable

The generated formats are:

  trait DefaultComtestgenerated_ServiceRequestableFormat extends scalaxb.XMLFormat[com.test.generated.ServiceRequestable] {
...
    
    def writes(__obj: com.test.generated.ServiceRequestable, __namespace: Option[String], __elementLabel: Option[String],
        __scope: scala.xml.NamespaceBinding, __typeAttribute: Boolean): scala.xml.NodeSeq = __obj match {
      case x: com.test.generated.MyRequestable => scalaxb.toXML[com.test.generated.MyRequestable](x, __namespace, __elementLabel, __scope, true)
      case x: com.test.generated.ServiceRequest => scalaxb.toXML[com.test.generated.ServiceRequest](x, __namespace, __elementLabel, __scope, false)
    }
  }

  trait DefaultComtestgenerated_MyRequestableFormat extends scalaxb.XMLFormat[com.test.generated.MyRequestable] {
...
    
    def writes(__obj: com.test.generated.MyRequestable, __namespace: Option[String], __elementLabel: Option[String],
        __scope: scala.xml.NamespaceBinding, __typeAttribute: Boolean): scala.xml.NodeSeq = __obj match {
      case x: com.test.generated.MySubRequest => scalaxb.toXML[com.test.generated.MySubRequest](x, __namespace, __elementLabel, __scope, true)
      case x: com.test.generated.MyRequest => scalaxb.toXML[com.test.generated.MyRequest](x, __namespace, __elementLabel, __scope, false)
    }
  }

Here is a very simple example to see the issue:

    val request: ServiceRequestable =
      MyRequest(
        IP = Some("127.0.0.1"),
        ID = 123,
      )

    val xml = toXML(
      request,
      "request",
      defaultScope
    )(Comtestgenerated_ServiceRequestableFormat)

The expected output would be:

<request xsi:type="MyRequest"
         xmlns:tns="http://tempuri.org/"
         xmlns:xs="http://www.w3.org/2001/XMLSchema"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <IP>127.0.0.1</IP>
    <ID>123</ID>
</request>

The issue is that xsi:type="MyRequest" is not present in the generated XML with the current version of ScalaXB.

Indeed, step by step, we have the following:

  • (1) In DefaultComtestgenerated_ServiceRequestableFormat#writes, we match on case x: com.test.generated.MyRequestable and call another toXML with typeAttribute=true
  • (2) in DefaultComtestgenerated_MyRequestableFormat#writes, we match on case x: com.test.generated.MyRequest and override typeAttribute to false

Proposal
Actually use the typeAttribute that is propagated. Indeed writes takes it as an argument, so for traits we can just reuse the value for default cases.

The generated code would be

  trait DefaultComtestgenerated_ServiceRequestableFormat extends scalaxb.XMLFormat[com.test.generated.ServiceRequestable] {
...
    
    def writes(__obj: com.test.generated.ServiceRequestable, __namespace: Option[String], __elementLabel: Option[String],
        __scope: scala.xml.NamespaceBinding, __typeAttribute: Boolean): scala.xml.NodeSeq = __obj match {
      case x: com.test.generated.MyRequestable => scalaxb.toXML[com.test.generated.MyRequestable](x, __namespace, __elementLabel, __scope, true)
      case x: com.test.generated.ServiceRequest => scalaxb.toXML[com.test.generated.ServiceRequest](x, __namespace, __elementLabel, __scope, __typeAttribute)
    }
  }

  trait DefaultComtestgenerated_MyRequestableFormat extends scalaxb.XMLFormat[com.test.generated.MyRequestable] {
...
    
    def writes(__obj: com.test.generated.MyRequestable, __namespace: Option[String], __elementLabel: Option[String],
        __scope: scala.xml.NamespaceBinding, __typeAttribute: Boolean): scala.xml.NodeSeq = __obj match {
      case x: com.test.generated.MySubRequest => scalaxb.toXML[com.test.generated.MySubRequest](x, __namespace, __elementLabel, __scope, true)
      case x: com.test.generated.MyRequest => scalaxb.toXML[com.test.generated.MyRequest](x, __namespace, __elementLabel, __scope, __typeAttribute)
    }
  }

In that case, the typeAttribute=true from step (1) would be reused in step (2) and the typeAttribute would actually be written.
The behavior of an operation taking a MyRequestable would not change (the typeAttribute would not be written in that case).

Note
Should solve #364 and I got inspired by #508

Copy link
Copy Markdown
Owner

@eed3si9n eed3si9n left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This makes more sense.

@eed3si9n eed3si9n merged commit 5c3bf4c into eed3si9n:master Oct 29, 2019
@eed3si9n eed3si9n changed the title Handling of typeAttribute for traits Populate xsi:type attribute on toXML(...) Jun 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants