pabigot/pyxb

Simple content with fixed value and attributes are not handled properly

maciekwawro opened this issue · 2 comments

I have something along the lines of schema.xsd:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com/" xml:lang="en">
  <xsd:element name="Element" fixed="ElementValue">
    <xsd:complexType>
      <xsd:simpleContent>
        <xsd:extension base="xsd:string">
          <xsd:attribute name="attribute" type="xsd:string" use="required" fixed="AttributeValue"/>
        </xsd:extension>
      </xsd:simpleContent>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

and the corresponding document.xml:

<?xml version="1.0" ?>
<example:Element xmlns:example="http://www.example.com/" attribute="AttributeValue">ElementValue</example:Element>

I could not find anything in specs that would forbid such declaration, and xmllint validates it fine:

$ xmllint --schema schema.xsd document.xml --noout
document.xml validates
$ xmllint --schema http://www.w3.org/2001/XMLSchema.xsd schema.xsd --noout
schema.xsd validates

However PyXB doesn't seem to handle that:

>>> import example
>>> xml = open('document.xml').read()
>>> data = example.CreateFromDocument(xml)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "example.py", line 65, in CreateFromDocument
    saxer.parse(io.BytesIO(xmld))
  File "/usr/lib/python2.7/xml/sax/expatreader.py", line 110, in parse
    xmlreader.IncrementalParser.parse(self, source)
  File "/usr/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse
    self.feed(buffer)
  File "/usr/lib/python2.7/xml/sax/expatreader.py", line 213, in feed
    self._parser.Parse(data, isFinal)
  File "/usr/lib/python2.7/xml/sax/expatreader.py", line 365, in end_element_ns
    self._cont_handler.endElementNS(pair, None)
  File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/saxer.py", line 388, in endElementNS
    binding_object = this_state.endBindingElement()
  File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/saxer.py", line 226, in endBindingElement
    self.__constructElement(self.__delayedConstructor, self.__attributes, args)
  File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/saxer.py", line 116, in __constructElement
    self.__bindingInstance = new_object_factory(*content, **kw)
  File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/basis.py", line 1631, in __call__
    args = [ self.compatibleValue(args[0], **kw) ]
  File "/usr/local/lib/python2.7/dist-packages/pyxb/binding/basis.py", line 1654, in compatibleValue
    raise pyxb.ElementChangeError(self, value)
pyxb.exceptions_.ElementChangeError: Value ElementValue for element {http://www.example.com/}Element incompatible with fixed content

Also see StackOverflow question.

Looks like _TypeBinding_mixin._CompatibleValue only handles the case where the element type is simple. With your schema it's a complex type with simple content, and it's returning an element of that type. In this case it should probably just return the content.

You can try patching your installation with something like this; I have no idea whether this change results in new bugs.

diff --git a/pyxb/binding/basis.py b/pyxb/binding/basis.py
index f624e0e6..1d433559 100644
--- a/pyxb/binding/basis.py
+++ b/pyxb/binding/basis.py
@@ -397,7 +397,10 @@ class _TypeBinding_mixin (utility.Locatable_mixin):
         # See if we have convert_string_values on, and have a string type that
         # somebody understands.
         if convert_string_values and value_type == six.text_type:
-            return cls(value)
+            rv = cls(value)
+            if isinstance(rv, complexTypeDefinition) and cls._IsSimpleTypeContent():
+                return rv.value()
+            return rv
 
         # Maybe this is a union?
         if issubclass(cls, STD_union):

Thanks. I don't know that either, but it certainly helps with this particular issue :)