Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Oct 29, 2019
Merged

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

merged 2 commits into from
Oct 29, 2019

Conversation

abestel
Copy link

@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
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
This pull request was closed.
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