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

I get a "missing element label" exception when a try to serialize a xsd:choise element #138

Closed
dportabella opened this issue Feb 13, 2012 · 8 comments
Labels

Comments

@dportabella
Copy link
Contributor

I get a "missing element label" exception when I try to serialize a xsd:choise element.
This is a compact example illustrating the error:

example XSD with xs:choice:


<pre><code>
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://example.com/"
            xmlns:ex="http://example.com/"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            elementFormDefault="qualified" attributeFormDefault="qualified">

  <xsd:element name="customer" type="ex:Customer" />

  <xsd:complexType name="Customer">
    <xsd:sequence>
      <xsd:element name="name" type="xsd:string"/>
      <xsd:element name="id" type="xsd:string"/>
      <xsd:element name="address" type="ex:Address"/>
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="Address">
    <xsd:choice>
      <xsd:element name="internalAddress" type="ex:InternalAddress" />
      <xsd:element name="ExternalAddress" type="ex:ExternalAddress" />
    </xsd:choice>
  </xsd:complexType>

  <xsd:complexType name="InternalAddress">
    <xsd:sequence>
      <xsd:element name="line1" type="xsd:string"/>
      <xsd:element name="line2" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>

  <xsd:complexType name="ExternalAddress">
    <xsd:sequence>
      <xsd:element name="id1" type="xsd:integer"/>
      <xsd:element name="id2" type="xsd:integer"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

---
scalaxb test.xsd --package package_test --outdir src

---
import package_test.{InternalAddress, Address, Customer}

object main {
  def main(args: Array[String]) {
    val internalAddress = InternalAddress(line1 = "line111", line2 = "line222");
    println(internalAddress);
    // this works:
    println(scalaxb.toXML[package_test.InternalAddress](internalAddress, None, Some("internalAddress1"), package_test.defaultScope));

    val address = Address(scalaxb.DataRecord(internalAddress));
    println(address);
    // this thows: Exception in thread "main" java.lang.RuntimeException: missing element label.
    println(scalaxb.toXML[package_test.Address](address, None, Some("address1"), package_test.defaultScope));

    val customer = Customer(name = "john", id = "22", address = address);
    println(customer);
    // this throws: Exception in thread "main" java.lang.RuntimeException: missing element label.
    println(scalaxb.toXML[package_test.Customer](customer, None, Some("customer1"), package_test.defaultScope));
  }
}
@eed3si9n
Copy link
Owner

val address = Address(scalaxb.DataRecord(internalAddress));

Change the above to:

val address = Address(scalaxb.DataRecord(None, Some("internalAddress"), internalAddress));

@dportabella
Copy link
Contributor Author

Thanks for the answer.

However passing "internalAddress" as a string is not very safe (an error cannot be detected at compile time).

How about something similar to JAXB? for instance:

val address = Address().setInternalAddress(internalAddress);

however this is not a final class.

or:

case class Address(internalAddress: InternalAddress = None, externalAddress: ExternalAddress = None);

and you check that one and only one (internalAddres or externalAddress) is provided.

or maybe the easiest and safest could be:

Address.buildFromInternalAddress(internalAddress: InternalAddress) : Address = { ... }
Address.buildFromExternalAddress(externalAddress: ExternalAddress) : Address = { ... }

@eed3si9n
Copy link
Owner

I added #141 as feature request.

@dportabella
Copy link
Contributor Author

Thanks!
subtyping DataRecord looks good!

Another related question, could scalaxb be modified so that this works:
val internalAddress = InternalAddress(line1 = "line111", line2 = "line222");
val customer = Customer(name = "john", id="22", address = internalAddress);

that is, can the use of DataRecord be hidden to the user in this case?

is there a raison why this simple code could not work?

@eed3si9n
Copy link
Owner

scalaxb is a data binding tool, which prioritizes the schema, and XML input and output over the generated code. Basic assumption is that the schema author knew what he was doing, and I should stick to it. Otherwise, it won't be able to write back to the same XML afterwards.

In this simple example, Address is acting as dumb switch, but it could for example be derived to another complex type in other schema using import and inheritance.

@dportabella
Copy link
Contributor Author

could you modify the example XSD above to show a case where writing that simple code would not be possible?
maybe there is also a convenient and general solution for that case.

@eed3si9n
Copy link
Owner

Here's a sample project I created: https://github.com/eed3si9n/scalaxb-sample/tree/gh-138/gh138-sample

I derived another type of Address called TypedAddress:

  <xsd:complexType name="TypedAddress">
    <xsd:complexContent>
      <xsd:extension base="ex:Address">
        <xsd:sequence>
          <xsd:element name="addressType"   type="xsd:string"/>
        </xsd:sequence>
      </xsd:extension>
    </xsd:complexContent>
  </xsd:complexType>

This could be used as follows:

  val address2 = TypedAddress(scalaxb.DataRecord(None, Some("internalAddress"), internalAddress), "Billing")
  val customer2 = Customer(name = "john", id = "22", address = address2)

@bmontuelle
Copy link

Great job.

For the record it also works the same way for restriction on complexContent (also generate a DataRecord because defined as an extension of soap Array :

My wsdl looks like

        <s:complexType name="ArrayPdfArray">
                <s:complexContent>
                    <s:restriction base="soapenc:Array">
                        <s:attribute ref="soapenc:arrayType" wsdl:arrayType="tns:ArrayPdf[]"/>
                    </s:restriction>
                </s:complexContent>
            </s:complexType>

And consumer code

ArrayPdfArray(
            arraySequence1 = Some(ArraySequence(
              any2 = scalaxb.DataRecord(None, Some("ArrayPdf"),
                  ArrayPdf(
                  //Whatever properties ArrayPdf contains
                )
              )
            )),
            attributes = Map()
          )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants