user3685895
user3685895

Reputation: 33

Complex Assertion in XML schema

<xs:complexType name="scene">
    <xs:sequence>
        <xs:element name="subtitle" type="subtitle" minOccurs="0"/>
        <xs:choice maxOccurs="unbounded">
            <xs:element name="speech" type="speech"/>
            <xs:element name="stagedir" type="stagedir"/>
            <xs:element name="subhead" type="subhead"/>
        </xs:choice>
    </xs:sequence>
    <xs:attribute name="title" use="required"/>
</xs:complexType>

<xs:complexType name="speech">
    <xs:sequence>
        <xs:element name="line" type="line"/>
    </xs:sequence>
    <xs:attribute name="persona" use="required"/>
    <xs:attribute name="actorID" use="required"/>
</xs:complexType>  

It is scenario of a school play. Thus, one actor may portray multiple personae (characters). Similarly, many actors may be sharing lines of a single role. But to ensure that this doesn't go haywire. There must be constraint to check that within one scene element, an actor cannot be two characters, as this might cause a problem on stage. An actor can't talk to himself obviously. Though the actor can be someone else in a different scene(element).

So, If actorID = 15 is character 'C' in this scene he cannot be any other character for this scene but actorID = 17 can be character 'C' too.

Upvotes: 3

Views: 820

Answers (1)

C. M. Sperberg-McQueen
C. M. Sperberg-McQueen

Reputation: 25034

Welcome to Stack Overflow.

You don't say explicitly what your question is; I guess it's either "Why is my attempt to formulate this constraint not working?" -- which is hard to answer since you don't show us what you've tried -- or perhaps more generally "How do I formulate an assertion to check this constraint?" (For future reference, note that Stack Overflow readers tend to react more positively to questions that show an attempt to solve the problem on your own.)

Your formulation of the constraint (within one scene element, an actor cannot be two characters) is already in a form that's easy to express using XSD 1.1 assertions. To make it work, you will need to understand XPath 2.0 quantified expressions (expressions using the keywords every and satisifies or some and satisfies. You won't learn all about them here, but it should be easy to look them up using the keywords or the term 'quantified expression'.

First: where does the assertion go? XSD 1.1 assertions are associated with a type, and can use XPath expressions to point to nodes within an instance of that type, but not to any other nodes. So the assertion cannot be on the actorID or persona attributes, nor on the speech element, because then the assertion would have to point upwards in the tree to refer to the scene element. So you want an assertion on your scene element -- i.e. in your complex type scene.

And what should it say? Rephrasing it just a little: within this scene, every actor must play at most one persona -- and since we will only ever be talking about actorIDs that occur in the scene, we don't have to worry about actorIDs that play zero personae. So: within this scene, every value of speech/@actorID must be associated with exactly one value of speech/persona. The outline of the assertion can be

every $actor in speech/@actorID 
satisfies
(: this actor plays only one role in this scene :)

It remains to figure out how to formalize the sentence this actor plays only one role, or this actorID value is associated with exactly one persona value. We can get all the persona attributes in the scene with

./speech/@persona

And we can restrict the set to those associated with a given actorID $actor this way:

./speech[@actorID = $actor]/@persona

The constraint we want to apply to this set of persona attributes is simple: they should all have the same value. Or, equivalently: there must not be more than one distinct value for the set. That is: when we apply the distinct-values function to this set of values, we should get a singleton list back. So:

count(
  distinct-values(
    ./speech[@actorID = $actor]/@persona
  )
) eq 1

The entire assertion can thus be formulated:

(: every actor ID in this scene is associated
   with exactly one persona in this scene :)

every $actor in ./speech/@actorID 
satisfies
count(
  distinct-values(
    ./speech[@actorID = $actor]/@persona
  )
) eq 1

Put this in the test attribute of an xs:assert element, add that assertion to your complex type definition, and you're done. (Well, actually, having formulated this one, you may immediately think "Well, wait a minute. Shouldn't we also ensure that any given persona is played only by a single actor, either within a scene or within the entire play?" I seem to remember reading about productions with three actors playing Hamlet, all on stage at the same time and each taking some of the character's lines. But perhaps the school plays you're describing don't go in for such avant-garde approaches. And even if they do -- the speeches of Hamlet must be divided among the actors playing Hamlet, which probably means the character of Hamlet is divided among three personae.) But that one is just the mirror image of this one; I'll leave it to you as an exercise.

Upvotes: 2

Related Questions