Hi Jeff,
On 6 May 2010 23:03, Jeff Ortel <jortel(a)redhat.com> wrote:
Hey Martin,
Handling NULL and nil is tricky in XML. There is a big difference between
the presents/absence of a element and having the element be empty
<middleName/>. Handling these nodes depends on how they are defined in the
XSD.
Yes. However, if the WSDL says the complex type has a given attribute,
I think Suds should make sure that particular complex type *always*
has that attribute (even if set to None or possibly some other "no
value" marker). Getting an attribute error is pretty disconcerting.
Suds follows these rules which (I believe) directly reflect XSD/XML
specifications:
An /optional/ element that is omitted from the document is just that - not
specified. So, suds will not create an attribute for it in the unmarshalled
object.
Okay. I guess that runs contrary to my expectations, though I can see
how this may be the right interpretation.
A /required/ element that is omitted from the document is represented
in the
unmarshalled object as an attribute with a value = None. It's supposed to
be there and isn't. It's absence is interpreted as nil. Although
technically, it's required by the XSD so it's absence makes the document
invalid.
Right. So really, there's no way for the service to say "this is
optional" and the code using Suds to do a check like "if response.foo
is not None". You have to do hasattr/getattr tricks. That makes the
code more brittle, since you're relying on the string names of the
attributes when doing the check.
An /empty/ (nil) element <middleName xsi:nil="1"/>
contained in the document
is represented in the unmarshalled object as an attribute with a value =
None. But, this element must be defined in the xsd as <xs:element
xs:nillable="1"/>.
Okay. I suspect our WSDL doesn't do this. Maybe it should.
An /empty/ (non-nil) element <middleName/> contained in the
document is
represented in the unmarshalled object as an attribute with a value = ''.
None (nil) must be represented in the document with xsi:nil="1".
That's actually seems entirely wrong to me. The WSDL has specified a
type for this attribute. In the example below, the attribute is of a
type 'address', another complex type. For that to be a string is just
plain wrong, and makes for some pretty awkward duck typing.
> We are calling a service getUserDetails(). It returns a complex
type
> "userDetails" with various fields, let's say first/middle/last name to
> keep it simple. The WSDL says middleName is optional (it has
> <xs:element minOccurs='0' name='middleName'
type='xs:string'/>).
>
> Our code expects the return value to have a middleName attribute with
> a value of None if the middle name is not set.
Because it is optional (minOccurs='0'), suds will not create an attribute
when the node is not in the document.
I can see why Suds does this, though I think it makes client code more
awkward to write. So, in our case we're copying some user data to the
session. Before, we'd do:
session['middlename'] = response.middlename
and we'd expect that session['middlename'] could be None.
Since middlename has minOccurs=0, we actually need:
session['middlename'] = getattr(response, 'middlename', None)
Which is ugly. But even more so, if I'd done:
session['middlename'] = getattr(response, 'middlenmae', None)
(i.e. a typo) I'd get None always and this error could go silent unnoticed.
> However, what's happening is that the service (being written
by a
> third party, I believe using JBoss and Axis) is not returning anything
> at all for this attribute. The complex type simply doesn't have it, so
> when we do details.middleName, we get an AttributeError.
Right. It's optional so you need to check first.
I wish I could check using something other than getattr/hasattr, though.
> I can't post the WSDL or response XML here on a public list,
but
> basically it looks to me like the response is omitting the tag for the
> middle name altogether and Suds just ignores it, rather than
> consulting the WSDL and saying "this property should be here, it's not
> in the response, set it to None", as we were expecting.
Since <middleName/> is optional, it's not necessarily /supposed/ to be
there. It's optional so it /may/ be there.
Would there be a better way for the WSDL to say "not required"?
> Actually, there's one other problem: In the userDetails
complex type,
> we have an attribute that's of another complex type, say "address". If
> This is not set, it comes through as an empty tag "<address />" in
the
> response body. Suds treats that as an empty string, instead of None,
> as I would've expected.
As described above, empty does not always = None.
Yes, I appreciate that. In some frameworks, we have e.g.
OMITTED = object()
and then you basically can do:
if response.middlename is OMITTED:
...
or something like that. Of course, this has cons, too.
Cheers,
Martin