Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

can an XSD element be overloaded with multiple possible complex types?

Tags:

xml

xsd

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:

  1. 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.

  2. 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.

  3. 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?

like image 860
Edward Hamlin Avatar asked Jul 06 '11 23:07

Edward Hamlin


2 Answers

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.

like image 57
xcut Avatar answered Oct 23 '22 17:10

xcut


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.

like image 44
Michael Kay Avatar answered Oct 23 '22 17:10

Michael Kay