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

indirect restriction of xs:anySimpleType does not round trip #200

Closed
sagarcjoshi opened this Issue Mar 7, 2013 · 5 comments

Comments

Projects
None yet
2 participants
@sagarcjoshi

steps

  1. compile a schema with a complex type that extends xs:anySimpleType, and another complex type that restrict that.
    <xs:complexType name="anySimpleTypeExtension">
      <xs:simpleContent>
        <xs:extension base="xs:anySimpleType">
           <xs:anyAttribute namespace="##any" processContents="lax"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>

    <xs:complexType name="IndirectAnySimpleTypeRestriction">
      <xs:simpleContent>
        <xs:restriction base="gen:anySimpleTypeExtension">
          <xs:simpleType>
            <xs:union>
              <xs:simpleType>
                <xs:restriction base="xs:unsignedShort">
                  <xs:enumeration value="1"/>
                  <xs:enumeration value="2"/>
                </xs:restriction>
              </xs:simpleType>
              <xs:simpleType>
                <xs:restriction base="xs:unsignedShort">
                  <xs:enumeration value="0"/>
                </xs:restriction>
              </xs:simpleType>
            </xs:union>
          </xs:simpleType>
        </xs:restriction>
      </xs:simpleContent>
    </xs:complexType>
  1. try round tripping it.
    def testSimpleAnyTypeIndirectRestriction {
      println("testSimpleAnyTypeIndirectRestriction")
      val subject = <foo xmlns="http://www.example.com/general"
        xmlns:gen="http://www.example.com/general"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xs="http://www.w3.org/2001/XMLSchema">1</foo>
      val obj = fromXML[IndirectAnySimpleTypeRestriction](subject)

      def check(obj: Any) = obj match {
          case AnySimpleTypeRestriction(DataRecord(_, _, <foo>1</foo>), _) =>
          case _ => error("match failed: " + obj.toString)
        }
      check(obj)
      val document = toXML[IndirectAnySimpleTypeRestriction](obj, "foo", scope)
      println(document)
      check(fromXML[IndirectAnySimpleTypeRestriction](document))
    }

problem

  1. toXML dumps an artifact from DataRecord(...):
    <foo xmlns="http://www.example.com/general" xmlns:gen="http://www.example.com/general" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">DataRecord({http://www.example.com/general}foo,&lt;foo xmlns:xs=&quot;http://www.w3.org/2001/XMLSchema&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:gen=&quot;http://www.example.com/general&quot; xmlns=&quot;http://www.example.com/general&quot;&gt;1&lt;/foo&gt;)</foo>

expectation

  1. round trips correctly.
    <foo xmlns="http://www.example.com/general" xmlns:gen="http://www.example.com/general" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">1</foo>

original report

xs:anySimpleType gets converted into scalaxb.DataRecord[Any]. Is it right?
I gets difficult to extract value from this. e.g. In CIM_ResourceAllocationSettingData.xsd the value in ResourceType is simple Int/short value. But I am not able to extract it from DataRecord. The value member of this DataRecord returns an XML, instead of actual value.

DataRecord({http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData}ResourceType,<rasd:ResourceType xmlns:ns12="http://www.vmware.com/vcloud/v1.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vmext="http://www.vmware.com/vcloud/extension/v1.5" xmlns:vmw="http://www.vmware.com/schema/ovf" xmlns:vssd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:rasd="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ovf="http://schemas.dmtf.org/ovf/envelope/1" xmlns="http://www.vmware.com/vcloud/v1.5">3</rasd:ResourceType>)

Actual XML

<ovf:Item>
...
        <rasd:ResourceType>3</rasd:ResourceType>
...
    </ovf:Item>
@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Mar 7, 2013

Owner

You do have the access to the XML, so you should be able to say x.value.value.text.toInt where x is your resource type. That should give you the value. Are you expecting x.value.value to contain Int?
There are two things you could do.

declare the type in the XML document

<ovf:Item>
...
        <rasd:ResourceType xsi:type="xs:unsignedShort">3</rasd:ResourceType>
...
    </ovf:Item>

where xsi and xs maps to the appropriate namespaces.

override the default parser

override the default parser

trait CustomXMLProtocol extends XMLProtocol {
  override implicit lazy val DmtfResourceTypeFormat: scalaxb.XMLFormat[org.dmtf.ResourceType] = new CustomDmtfResourceTypeFormat {}

  trait CustomDmtfResourceTypeFormat extends DefaultDmtfResourceTypeFormat {
    override def reads(seq: scala.xml.NodeSeq, stack: List[scalaxb.ElemName]): Either[String, org.dmtf.ResourceType] = seq match {
      case node: scala.xml.Node => Right(org.dmtf.ResourceType(scalaxb.fromXML[scalaxb.DataRecord[Int]](node, scalaxb.ElemName(node) :: stack),
        scala.collection.immutable.ListMap((node match {
          case elem: scala.xml.Elem =>
            elem.attributes.toList flatMap {

              case scala.xml.UnprefixedAttribute(key, value, _) =>
                List(("@" + key, scalaxb.DataRecord(None, Some(key), value.text)))
              case scala.xml.PrefixedAttribute(pre, key, value, _) =>
                val ns = elem.scope.getURI(pre)
                List(("@{" + ns + "}" + key, scalaxb.DataRecord(Option[String](ns), Some(key), value.text)))
              case _ => Nil
            }
          case _ => Nil
        }): _*)))
      case _ => Left("reads failed: seq must be scala.xml.Node")
    }
  }
}

note

Here's the definition of ResourceType element:

<xs:element name="ResourceType" nillable="true">
<xs:complexType>
<xs:simpleContent>
<xs:restriction base="cim:cimAnySimpleType">
<xs:simpleType>
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<xs:enumeration value="4"/>
<xs:enumeration value="5"/>
<xs:enumeration value="6"/>
<xs:enumeration value="7"/>
<xs:enumeration value="8"/>
<xs:enumeration value="9"/>
<xs:enumeration value="10"/>
<xs:enumeration value="11"/>
<xs:enumeration value="12"/>
<xs:enumeration value="13"/>
<xs:enumeration value="14"/>
<xs:enumeration value="15"/>
<xs:enumeration value="16"/>
<xs:enumeration value="17"/>
<xs:enumeration value="18"/>
<xs:enumeration value="19"/>
<xs:enumeration value="20"/>
<xs:enumeration value="21"/>
<xs:enumeration value="22"/>
<xs:enumeration value="23"/>
<xs:enumeration value="24"/>
<xs:enumeration value="25"/>
<xs:enumeration value="26"/>
<xs:enumeration value="27"/>
<xs:enumeration value="28"/>
<xs:enumeration value="29"/>
<xs:enumeration value="30"/>
<xs:enumeration value="31"/>
<xs:enumeration value="32"/>
<xs:enumeration value="33"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:enumeration value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="34"/>
<xs:maxInclusive value="32767"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="32768"/>
<xs:maxInclusive value="65535"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:anyAttribute namespace="##any" processContents="lax"/>
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
</xs:element>

This is a xs:element whose content is an anonymous xs:complexType with xs:simpleContent and xs:anyAttribute that restricts cim:cimAnySimpleType. Here's cim:cimAnySimpleType's definition:

  <xs:complexType name="cimAnySimpleType">
    <xs:simpleContent>
      <xs:extension base="xs:anySimpleType">
         <xs:anyAttribute namespace="##any" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

It's currently generating:

case class ResourceType(value: scalaxb.DataRecord[Any],
  attributes: Map[String, scalaxb.DataRecord[Any]]) extends CimAnySimpleTypable

Since Scala mostly supports extension by inheritance, but not restriction, the ResourceType above is appropriate.

  trait DefaultDmtfResourceTypeFormat extends scalaxb.XMLFormat[org.dmtf.ResourceType] with scalaxb.CanWriteChildNodes[org.dmtf.ResourceType] {
    ...
    def reads(seq: scala.xml.NodeSeq, stack: List[scalaxb.ElemName]): Either[String, org.dmtf.ResourceType] = seq match {
      case node: scala.xml.Node => Right(org.dmtf.ResourceType(scalaxb.fromXML[scalaxb.DataRecord[Any]](node, scalaxb.ElemName(node) :: stack),
        scala.collection.immutable.ListMap((node match {
          case elem: scala.xml.Elem =>
            elem.attributes.toList flatMap {

              case scala.xml.UnprefixedAttribute(key, value, _) =>
                List(("@" + key, scalaxb.DataRecord(None, Some(key), value.text)))
              case scala.xml.PrefixedAttribute(pre, key, value, _) =>
                val ns = elem.scope.getURI(pre)
                List(("@{" + ns + "}" + key, scalaxb.DataRecord(Option[String](ns), Some(key), value.text)))
              case _ => Nil
            }
          case _ => Nil
        }): _*)))
      case _ => Left("reads failed: seq must be scala.xml.Node")
    }

The eventual parsing is done by DataRecord.fromAny, which uses xsi:type attribute to figure out the explicit type of the value. Since the schema does state that the value of ResourceType is an union of xs:unsignedShort and its derivatives, it could have tried to guess that it should map everything to Int.

But doing so probably would be inconsistent with the rest of the data binding since xs:simpleTypes with xs:enumeration restriction is normally handled by scalaxb by generating case objects.

Owner

eed3si9n commented Mar 7, 2013

You do have the access to the XML, so you should be able to say x.value.value.text.toInt where x is your resource type. That should give you the value. Are you expecting x.value.value to contain Int?
There are two things you could do.

declare the type in the XML document

<ovf:Item>
...
        <rasd:ResourceType xsi:type="xs:unsignedShort">3</rasd:ResourceType>
...
    </ovf:Item>

where xsi and xs maps to the appropriate namespaces.

override the default parser

override the default parser

trait CustomXMLProtocol extends XMLProtocol {
  override implicit lazy val DmtfResourceTypeFormat: scalaxb.XMLFormat[org.dmtf.ResourceType] = new CustomDmtfResourceTypeFormat {}

  trait CustomDmtfResourceTypeFormat extends DefaultDmtfResourceTypeFormat {
    override def reads(seq: scala.xml.NodeSeq, stack: List[scalaxb.ElemName]): Either[String, org.dmtf.ResourceType] = seq match {
      case node: scala.xml.Node => Right(org.dmtf.ResourceType(scalaxb.fromXML[scalaxb.DataRecord[Int]](node, scalaxb.ElemName(node) :: stack),
        scala.collection.immutable.ListMap((node match {
          case elem: scala.xml.Elem =>
            elem.attributes.toList flatMap {

              case scala.xml.UnprefixedAttribute(key, value, _) =>
                List(("@" + key, scalaxb.DataRecord(None, Some(key), value.text)))
              case scala.xml.PrefixedAttribute(pre, key, value, _) =>
                val ns = elem.scope.getURI(pre)
                List(("@{" + ns + "}" + key, scalaxb.DataRecord(Option[String](ns), Some(key), value.text)))
              case _ => Nil
            }
          case _ => Nil
        }): _*)))
      case _ => Left("reads failed: seq must be scala.xml.Node")
    }
  }
}

note

Here's the definition of ResourceType element:

<xs:element name="ResourceType" nillable="true">
<xs:complexType>
<xs:simpleContent>
<xs:restriction base="cim:cimAnySimpleType">
<xs:simpleType>
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:enumeration value="1"/>
<xs:enumeration value="2"/>
<xs:enumeration value="3"/>
<xs:enumeration value="4"/>
<xs:enumeration value="5"/>
<xs:enumeration value="6"/>
<xs:enumeration value="7"/>
<xs:enumeration value="8"/>
<xs:enumeration value="9"/>
<xs:enumeration value="10"/>
<xs:enumeration value="11"/>
<xs:enumeration value="12"/>
<xs:enumeration value="13"/>
<xs:enumeration value="14"/>
<xs:enumeration value="15"/>
<xs:enumeration value="16"/>
<xs:enumeration value="17"/>
<xs:enumeration value="18"/>
<xs:enumeration value="19"/>
<xs:enumeration value="20"/>
<xs:enumeration value="21"/>
<xs:enumeration value="22"/>
<xs:enumeration value="23"/>
<xs:enumeration value="24"/>
<xs:enumeration value="25"/>
<xs:enumeration value="26"/>
<xs:enumeration value="27"/>
<xs:enumeration value="28"/>
<xs:enumeration value="29"/>
<xs:enumeration value="30"/>
<xs:enumeration value="31"/>
<xs:enumeration value="32"/>
<xs:enumeration value="33"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:union>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:enumeration value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="34"/>
<xs:maxInclusive value="32767"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:simpleType>
<xs:restriction base="xs:unsignedShort">
<xs:minInclusive value="32768"/>
<xs:maxInclusive value="65535"/>
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
<xs:anyAttribute namespace="##any" processContents="lax"/>
</xs:restriction>
</xs:simpleContent>
</xs:complexType>
</xs:element>

This is a xs:element whose content is an anonymous xs:complexType with xs:simpleContent and xs:anyAttribute that restricts cim:cimAnySimpleType. Here's cim:cimAnySimpleType's definition:

  <xs:complexType name="cimAnySimpleType">
    <xs:simpleContent>
      <xs:extension base="xs:anySimpleType">
         <xs:anyAttribute namespace="##any" processContents="lax"/>
      </xs:extension>
    </xs:simpleContent>
  </xs:complexType>

It's currently generating:

case class ResourceType(value: scalaxb.DataRecord[Any],
  attributes: Map[String, scalaxb.DataRecord[Any]]) extends CimAnySimpleTypable

Since Scala mostly supports extension by inheritance, but not restriction, the ResourceType above is appropriate.

  trait DefaultDmtfResourceTypeFormat extends scalaxb.XMLFormat[org.dmtf.ResourceType] with scalaxb.CanWriteChildNodes[org.dmtf.ResourceType] {
    ...
    def reads(seq: scala.xml.NodeSeq, stack: List[scalaxb.ElemName]): Either[String, org.dmtf.ResourceType] = seq match {
      case node: scala.xml.Node => Right(org.dmtf.ResourceType(scalaxb.fromXML[scalaxb.DataRecord[Any]](node, scalaxb.ElemName(node) :: stack),
        scala.collection.immutable.ListMap((node match {
          case elem: scala.xml.Elem =>
            elem.attributes.toList flatMap {

              case scala.xml.UnprefixedAttribute(key, value, _) =>
                List(("@" + key, scalaxb.DataRecord(None, Some(key), value.text)))
              case scala.xml.PrefixedAttribute(pre, key, value, _) =>
                val ns = elem.scope.getURI(pre)
                List(("@{" + ns + "}" + key, scalaxb.DataRecord(Option[String](ns), Some(key), value.text)))
              case _ => Nil
            }
          case _ => Nil
        }): _*)))
      case _ => Left("reads failed: seq must be scala.xml.Node")
    }

The eventual parsing is done by DataRecord.fromAny, which uses xsi:type attribute to figure out the explicit type of the value. Since the schema does state that the value of ResourceType is an union of xs:unsignedShort and its derivatives, it could have tried to guess that it should map everything to Int.

But doing so probably would be inconsistent with the rest of the data binding since xs:simpleTypes with xs:enumeration restriction is normally handled by scalaxb by generating case objects.

@sagarcjoshi

This comment has been minimized.

Show comment
Hide comment
@sagarcjoshi

sagarcjoshi Mar 7, 2013

The problem is with the marshaling. I unmarshal the XML into ovf:Item object and then make changes and marshal it again into XML. This is what I get -

<class:ResourceType>DataRecord({http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData}ResourceType,&lt;rasd:ResourceType xmlns:ns12=&quot;http://www.vmware.com/vcloud/v1.5&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:vmext=&quot;http://www.vmware.com/vcloud/extension/v1.5&quot; xmlns:vmw=&quot;http://www.vmware.com/schema/ovf&quot; xmlns:vssd=&quot;http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData&quot; xmlns:rasd=&quot;http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData&quot; xmlns:ovf=&quot;http://schemas.dmtf.org/ovf/envelope/1&quot; xmlns=&quot;http://www.vmware.com/vcloud/v1.5&quot;&gt;3&lt;/rasd:ResourceType&gt;)</class:ResourceType>

I am not touching the ResourceType element. I am changing other element values.
Shouldnt it marshal back to proper XML?

The problem is with the marshaling. I unmarshal the XML into ovf:Item object and then make changes and marshal it again into XML. This is what I get -

<class:ResourceType>DataRecord({http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData}ResourceType,&lt;rasd:ResourceType xmlns:ns12=&quot;http://www.vmware.com/vcloud/v1.5&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns:vmext=&quot;http://www.vmware.com/vcloud/extension/v1.5&quot; xmlns:vmw=&quot;http://www.vmware.com/schema/ovf&quot; xmlns:vssd=&quot;http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData&quot; xmlns:rasd=&quot;http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData&quot; xmlns:ovf=&quot;http://schemas.dmtf.org/ovf/envelope/1&quot; xmlns=&quot;http://www.vmware.com/vcloud/v1.5&quot;&gt;3&lt;/rasd:ResourceType&gt;)</class:ResourceType>

I am not touching the ResourceType element. I am changing other element values.
Shouldnt it marshal back to proper XML?

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Mar 7, 2013

Owner

Yes, it should round trip back to a proper XML document. So, this is not an extraction problem, but rather a round trip problem?

Owner

eed3si9n commented Mar 7, 2013

Yes, it should round trip back to a proper XML document. So, this is not an extraction problem, but rather a round trip problem?

@sagarcjoshi

This comment has been minimized.

Show comment
Hide comment
@sagarcjoshi

sagarcjoshi Mar 7, 2013

Yes, thats the problem. I can read the ResourceType value somehow. But this marshaling error becomes a show stopper.

Yes, thats the problem. I can read the ResourceType value somehow. But this marshaling error becomes a show stopper.

@eed3si9n

This comment has been minimized.

Show comment
Hide comment
@eed3si9n

eed3si9n Mar 13, 2013

Owner

I have not confirmed with the vCloud API, but I did fix what I think is the cause of the issue.
1.0.2-SNAPSHOT is out with the fix.

Owner

eed3si9n commented Mar 13, 2013

I have not confirmed with the vCloud API, but I did fix what I think is the cause of the issue.
1.0.2-SNAPSHOT is out with the fix.

eed3si9n added a commit that referenced this issue Apr 21, 2013

eed3si9n added a commit that referenced this issue May 20, 2013

eed3si9n added a commit that referenced this issue May 26, 2013

eed3si9n added a commit that referenced this issue Jul 18, 2013

eed3si9n added a commit that referenced this issue Jul 28, 2013

eed3si9n added a commit that referenced this issue Sep 12, 2013

eed3si9n added a commit that referenced this issue Nov 8, 2014

eed3si9n added a commit that referenced this issue Jul 7, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment