Creating an XMLObject

Brent Putman putmanb at georgetown.edu
Mon Oct 5 17:36:48 EDT 2015



On 10/5/15 4:25 PM, Camel Guy wrote:
>
>
> My document looks like:
>
> <n:attributes xmlns:n='foo'>
>   <n:attribute n:name='AcctId' n:value='000004D' />
> </n:attributes>
>

That's fine.  Since it's not a known, supported schema, you'll need to
change things up slightly from the basic XMLObjectSupport example.

> This is just a sample document. I put everything in a namespace to try
> to work around the error I've run into (read on..).
>

Since it's going into a namespace-qualified XML environment (SAML), it
pretty much has to be namespace-qualified XML.  Mixing the two doesn't
work very well.


>
> I'm getting the same error as described in
> http://shibboleth.net/pipermail/users/2015-July/022646.html
>

Ooops.  I was on vacation then, and looks like neither I nor anyone else
responded.  Hopefully if Alessio is still on the list, he'll accept my
belated apology for missing his post.


> A bit of the stack trace:
>
> net.shibboleth.utilities.java.support.logic.ConstraintViolationException:
> Local name cannot be null or empty
> at
> net.shibboleth.utilities.java.support.logic.Constraint.isNotNull(Constraint.java:227)
> ~[java-support-7.1.1.jar:na]
> at
> net.shibboleth.utilities.java.support.xml.QNameSupport.constructQName(QNameSupport.java:79)
> ~[java-support-7.1.1.jar:na]
> at
> net.shibboleth.utilities.java.support.xml.QNameSupport.getNodeQName(QNameSupport.java:102)
> ~[java-support-7.1.1.jar:na]
> at
> org.opensaml.core.xml.io.UnmarshallerFactory.getUnmarshaller(UnmarshallerFactory.java:86)
> ~[opensaml-core-3.1.1.jar:na]
> at
> org.opensaml.core.xml.io.UnmarshallerFactory$getUnmarshaller.call(Unknown
> Source) ~[na:na]
>


Well, that error is happening because: The XML is namespace-qualified,
but you haven't set the 'namespaceAware' property on the
DocumentBuilderFactory to 'true'.  So it's not parsing in a
namespace-aware way.  That is mandatory to do in this case.  It's
nothing to do with Shibboleth/OpenSAML.  You have to set that if you're
going to parse namespace-qualified XML with a JAXP DocumentBuilder,
period.  FYI, our ParserPool impls do that by default, since we're
always dealing with namespaces.


Setting that would get you past that error.  However, then you're going
to get another error because your element QName isn't a registered/known
element type.  I thought somewhere we had code that would automatically
return an XSAny for unknown element types, but apparently not in these
particular helper methods.  So you have to deal with it specifically.

Since you know in advance that this is the case, you just ask for that
kind of unmarshaller a-priori.  Combining that with use of our global
ParserPool, a variant of this will work:


// The DBF via the global ParserPool here is already namespace-aware.
Document doc =
XMLObjectProviderRegistrySupport.getParserPool().parse(new
StringReader(xmlString));
           
// Get the unmarshaller specifically for the default provider (XSAny)
Unmarshaller unmarshaller = XMLObjectSupport.getUnmarshaller(
      XMLObjectProviderRegistrySupport.getDefaultProviderQName());
           
// Now just unmarshall the root element.  This will be an instance of XSAny.
XMLObject xmlObject = unmarshaller.unmarshall(doc.getDocumentElement());


3 lines of code, so not so bad...

If you're just going to hand this to an XMLObjectAttributeValue, then
you're done.  If you actually want to work with it, mutate it, etc,
you'd cast xmlObject to XSAny and use its API.

As I mentioned, as an alternative to string building and parsing, etc,
you could just create an XSAny directly, and set its values.  This is a
cleaner and more performant approach (the parsing above is relatively
expensive).  Example:


// The outer <attributes> element
XSAny attributes = (XSAny) XMLObjectSupport.getBuilder(
    XMLObjectProviderRegistrySupport.getDefaultProviderQName())
    .buildObject(new QName("foo", "attributes", "n"));
           
// The inner <attribute> element
XSAny attribute = (XSAny) XMLObjectSupport.getBuilder(
      XMLObjectProviderRegistrySupport.getDefaultProviderQName())
      .buildObject(new QName("foo", "attribute", "n"));
           
// Add attribs on the inner element
attribute.getUnknownAttributes().put(new QName("foo", "name", "n"),
"AcctId");
attribute.getUnknownAttributes().put(new QName("foo", "value", "n"),
"000004D");
           
// Add the inner element as child of the outer element
attributes.getUnknownXMLObjects().add(attribute);


You can build up pretty much any XML structure that way as an XMLObject,
albeit a little inefficiently and verbosely.

Finally, if you were gong to do this a lot, use in different places,
etc, it would make more sense to just implement custom XMLObject
provider for your XML type(s).


--Brent



-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://shibboleth.net/pipermail/users/attachments/20151005/c9ad6368/attachment.html>


More information about the users mailing list