I have a schema that defines an event record that is used to convey a message between two systems in a publish/subscribe scenario. The event includes a "payload" element, event_data, with the details of the message. The complication is that the payload could be any of 30+ types, each defined in its own XSD.
For example (stripped down):
<event>
<event_name>new_phone_number</event_name>
<event_data>
<areacode>303</areacode>
<number>555-1212</number>
<extension>31</extension>
</event_data>
</event>
In this case, the event_data is of type phone_number, which is defined elsewhere in an imported XSD. But what I want to do is use the same mechanism to carry other kinds of structured message data. E.g., maybe it's a job change event, defined as type job_details:
<event>
<event_name>new_job</event_name>
<event_data>
<job_title>CEO</job_title>
<start_date>01/01/2012</start_date>
<location>Main Office</location>
</event_data>
</event>
The inner record, stored in the event_data element, is of type job_details, as defined in an imported XSD. The "outer" record, of type event, is little more than a way to carry the payload contained in the "inner" record.
So far I have looked at three ways to attack this, each with problems:
Use a "choice" construct that lists all the possible record types. The problem seems to be that it's not natural xml/xsd to have all entries in the choice list share the same element name.
I guess that rather than trying to include a structured subrecord in the event_data element, I could simply have an optional attribute reflecting every possible subrecord type. Each would have a unique name, of course, so you'd have an attribute for new_phone_number, one for new_job, and so on. Besides the potential maintainability and ugliness issues here, I'm not sure how I could enforce that one and only one of the attributes gets passed in a given instance of an event. I could live with that, but it makes the code fragile.
Someone answered a similar question by suggesting use of a union - but that seems to apply only to simple types. Wouldn't cover my use case.
Stumped! Any guidance?
You can do this by using an xsi:type
override in your instance document. XML processors cannot "infer" types, you need to provide this. Example:
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<event>
<event_name>new_phone_number</event_name>
<event_data xsi:type="PhoneNumberData">
<areacode>303</areacode>
<number>555-1212</number>
<extension>31</extension>
</event_data>
</event>
</root>
Note that you have to define a base complex type EventData
and PhoneNumberData
must derive from this, for this to work.
In XSD 1.1 (implemented in Xerces and Saxon), you can do this using the feature called "conditional type assignment", whereby at the level of an element declaration you decide which type to use from a list of alternatives selected by applying an XPath expression to the element's attributes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With