Reputation: 2739
We have need to populate every ID attribute in an XML file. That file happens to be a DITA topic, and DITA defines features for specialization (sort of like inheritance).
Because of that option to inherit, to future proof our system we want to 'discover' where the ID attribute is valid, but I cannot find any examples on how to discover optional attributes programmatically.
Does anyone have an example using Java or Saxon. We are open to XSLs or using the saxon java API. We just want to basically take XML file X
and find all nodes in X where the attribute "id"
is valid.
Upvotes: 0
Views: 96
Reputation: 163468
With Saxon, if you validate an element against a schema, then saxon:type [1] will give you its type annotation. So (using XQuery syntax, but you can equally well use XSLT):
let $e := validate(<e/>)
let $type-of-e := saxon:type($e)
Then to get the attributes defined on this type:
let $attUses := $type-of-e("attribute uses")
or if you just want the optional ones:
let $optionalAtts := $attUses[not(.("required"))]
and if you want the QNames of the optional attributes:
let $optionalAttributeNames := $optionalAtts ! .("attribute declaration") ! QName(.("target namespace"), .("name"))
What this is doing is to navigate around the schema component model. You will need to familiarise yourself with the description of this model in XSD part 1. Where XSD says, for example, that an "attribute use" component has a property named {attribute declaration}
, then in Saxon the attribute use is represented by a function, which can be called with the parameter "attribute declaration" to return the attribute declaration component (which is itself another function).
Let's try and take this a step further and look at your specific task, finding all nodes where an id attribute would be valid. If we assume that your instance document is already valid against the schema, and that the whole document carries type annotations as a result of validation against the schema, then you can define:
declare function f:allows-id($e as element(*)) as xs:boolean {
let $type-of-e := saxon:type($e)
let $attUses := $type-of-e("attribute uses")
let $attNames := $attUses ! .("attribute declaration") ! QName(.("target namespace"), .("name"))
return $attNames = QName("", "id")
}
and then you can find all elements that allow an id attribute but don't have one as
//*[f:allows-id(.) and not(@id)]
One minor observation: this won't find elements where an @id attribute would be valid as a result of wildcards in the schema (xs:anyAttribute), or where there is an open content model: it will only find those where the id attribute is explicitly declared in the type definition.
[1] http://www.saxonica.com/documentation/#!functions/saxon/type
Upvotes: 1