-
Notifications
You must be signed in to change notification settings - Fork 0
Some comments on XProc 1.1 (2017 01 22)
This paper is intended as a contribution to XProc workshop 7/8 February 2017, in Prague.
I will try to
-
give an overview of the new features introduced in XProc 1.1,
-
share some of my thoughts on these features, especially on the points which may be discussed, and
-
give hints on the aspect of backward compatibility, i.e. the ability of an XProc 1.0 pipeline to execute properly when a pipeline author changes her @version from "1.0" to "1.1".
The focus is clearly on what is in the specs as opposed to what is missing.
The basic structure of this paper is taken from Norm Walsh’s talk "Standards update: XProc 2.0" delivered at XML Prague 2015, though I changed the order of the topics discussed. The reference point is of course the latest pair of specification documents (build 5 of 2016-12-17).
Disclaimer: Not (all) my thoughts, but a synopsis of discussions I had and contributions I read.
Lets start with a simple, but nevertheless very useful syntactic enhancement: Instead of writing
<p:input port="source">
<p:inline>
<this-is-a-document />
</p:inline>
</p:input>we can now leave out the p:inline in most cases:
[…] if a single element node, optionally preceded and/or followed by whitespace, in any namespace other than the XProc namespace, occurs where a p:inline is allowed, it is treated as if it was enclosed within a p:inline element (with no attributes) […] An explicit p:inline is required if the author wants to include top level comments, processing instructions, or whitespace, or if the document element is in the XProc namespace. (See section 5.10.3: Implicit inlines)
We can even do this on a input port which allows sequences:
<p:input port="source">
<doc-1 />
<doc-2 />
<doc-3 />
</p:input>because more than one p:inline is allowed as child of p:input. I think this is a great enhancement which should not break anything with respect to backward compatibility.
The second basic improvement from my perspective are attribute value templates, i.e. the ability to use XPath expressions in attribute short cuts, so instead of
<p:count>
<p:with-option name="limit" select="$a+1" />
</p:count>we can now say
<p:count limit="{$a+1}" />This will also remove a lot of typing from pipeline authors. The explicit form will only be necessary if a context node (a port binding) is needed to determine the value of the option, because attribute value templates are evaluated with "an empty context node" (see section 2.10.1 Attribute Value Templates, last sentences of paragraph 3).
There should be only very rare problems with backward compatibility from this new feature. As the specs invented a new escaping mechanism, using curly bracket, there might be some unexpected results for XProc 1.0 pipelines using literal curly brackets, but this seems to me a very rare case.
Along with attribute value templates, XProc 1.1 introduces text value templates, which allow us to make use of the XPath context (options and variables) in constructing documents on a p:input port. Example (taken from Norm Walsh’s talk at XML Prague 2015):
<p:store href="{$somewhere}">
<p:input port="source">
<error>
<code>{$errCode}</code>
<msg>{$errMsg}</msg>
</error>
</p:input>
</p:store>Now this is obviously a great enhancement because producing a document with values from options and variables in elements was very hard in XProc 1.0. To disable this feature one can use @expand-text="false", but then a p:inline has to be wrapped around the document.
|
Note
|
Discuss @expand-text on the pipeline root elements. |
If I remember correctly, in earlier versions of the specs there was also a @expand-text defined on p:input, so one doesn’t have to wrap the document to prevent tvt processing and even a @expand-text on p:declare-pipeline, p:library and p:pipeline. One might doubt whether we need the attribute on p:input, but from my point of view there are good reasons to have the attribute on the pipeline’s root element. A pipeline author may change here @version="1.0" declaration to "1.1" and just add "@expand-text='false'" and then does not have to worry about any potential literal curly brackets inside created documents. This would, I think, a great aid to backward compatibility, which comes at literally no cost.
p:option and p:variable now have a new option attribute "as" which takes XPathSequenceType as its value and thereby declares the type for this option/variable. As in XProc 1.0 all options/variables are xs:string or xs:untypedAtomic this new feature gives pipeline authors more control, because now an error is raised (err:XD1001), if I declare a variable as xs:integer, but assign a value that does not match this declaration, say the string 'I am no integer'.
The other important improvement is that I now can declare a variable as element() or as document-node(), give it an appropriate value and then go on to process it in the same way as in XSLT or XQuery.
As the @as attribute is newly introduced on p:variable, p:option and p:with-option there should not be any problems with backward compatibility. In very rare cases problems may appear, when a pipeline developed for XProc 1.0 runs with a step library using XDM, especially regarding boolean values. Sometimes "yes", "no", "True" etc. are taken to be legitimate boolean values (or text representations of those values), but if a step (in XProc 1.1) declares an option as xs:boolean, an error should be raised.[1]
|
Note
|
Discuss clarification for @as on p:variable and p:option.
|
The specs say: "If an atomic type, or sequence of atomic types, is specified, the value provided for the option will be atomized according to the standard XPath rules." (5.5.1 p:variable und 5.5.2 p:option) I think this needs some clarification as to what is meant by the term "standard XPath rules". And what about casting? I think we should borrow from XSLT 3.0 and say: "If the as attribute is specified, then the supplied value of the variable is converted to the required type, using the function conversion rules."
|
Note
|
Discuss @as on p:with-option and err:XD1001.
|
The attribute "as" might not only appear on p:option, where the option is declared, a type is assigned and some default value is set, but also on p:with-option where the option is given an actual value when a step is invoked. There is no further explanation as what the attribute means here and how it is related to @as on p:option. I guess what is intended is the same processing as in XSLT with xsl:with-param and xsl:param: The supplied value is first transformed into an instance of the type on the p:with-option and if this can be done without an error, the resulting value is then transformed into the type on the p:option.
If this processing is intended, we should add a text explaining this. If not, we should explain it too. In both cases I think throwing an err:XD1001 with the text
It is a dynamic error if the computed value does not match the specified sequence type.
can be very confusing, because it is not clear, whether the value does not match the sequence type on p:with-option or on the p:option. I would argue to have two error messages, one when the value does not match the sequence type on p:with-option and one for p:option. But may be we can come up with some clever text, so a pipeline developer will know, she has two look in two places to find the reason for the error message.
I think we all once (or even more often) ran into an error raised because of an unbound parameter port on p:xslt, even though we know our stylesheet not to have any parameters. And we all may recall the problems explaining the differences between a regular port and a parameter port. This all is gone: Instead of magical parameter ports, p:xslt now has an option called "parameters" and this takes any "map(xs:QName,item())" as its value. Maps are also used on the following standard library steps:
-
p:parameters,
-
p:set-properties,
-
p:xslt,
-
p:hash,
-
p:template,
-
p:validate-with-schematron,
-
p:www-form-urlencode,
-
p:xquery,
-
p:hash and
-
p:xsl-formatter.
Along with parameter port of course p:with-param is gone. I think this is a great improvement, but the cost in terms of backward compatibility is high: As the signatures of the above named steps change any XProc 1.0 pipeline has to be changed using the new signatures and maps.[2] There is a new function p:make-map to give some help: It takes a c:param-set document or element as a parameter and will return a map that is suitable for the new step option. A second problem will arise for implementers: How do we expose maps to the pipeline environment, so pipeline users can set a map-typed option with a CLI or GUI?
|
Note
|
Discuss XPath version used in XProc. |
A third problem raises from the fact, that maps and map-related XPath functions are introduced in XPath 3.1. This has consequences for the specs, as we -at least to my reading- have to change the default XPath version used in XProc from 2.0 (see 2.7 XPaths in XProc) to 3.1, so we use the map type and also have functions producing and changing maps. This raises the burden of implementation, because XPath 3.0 and XPath 3.1 introduce a lot of new functions which need to be implemented alongside XProc. An alternative might be to say, that we want XDM 2.0 + map() and the XPath 2.0 function library + map related functions of XPath 3.1. But that would probably cause confusion because some will try to use an XPath-function from version 3.0 or 3.1. and then run into errors.
In order to get rid of some syntactic hurdles, the specs removed p:iteration-source, p:viewport-source and p:xpath-context as special connection providers p:for-each, p:viewport and p:choose (and in that line also from p:when). What comes in as a replacement is an unnamed input port, which is connected by simply using an ordinary p:input but without @port as a child of p:for-each and p:viewport. On p:choose and p:when the same construct is used to set the context item for the test expression. And: As a convenience to pipeline authors, p:input without a @port can now also be used to bind the primary input port of every step.
I think this is a step forward in order to unify XProc’s syntax. Of course it will break backward compatibility because an XProc 1.1 system will expect the new binding mechanism and raise an error if one of the old p:viewport-source etc. will occur. And there is one more thing. Compare:
The p:viewport-source is an anonymous input: its connection provides a single document to the p:viewport step. If no document is explicitly provided, then the viewport source is read from the default readable port. It is a dynamic error (err:XD0003) if the viewport source does not provide exactly one document. (XProc 1.0)
The p:viewport has a single anonymous input: its connection is provided by the p:input. If no document is explicitly provided, then the viewport source is read from the default readable port. If the p:viewport input is a sequence, each document in the sequence is processed in turn producing a sequence on the output. (XProc 1.1)
Now we can use p:viewport on a sequence of documents and do not need a p:for-each around it any longer!
While we were restricted to one p:catch to deal with all the possible errors raised in a p:try we can now have as many as we like or need, each one having a @code with a list of all the errors handled in this p:catch. This will certainly reduce our typing effort dealing with errors: In future the XProc engine will take care of delivering the errors to the correct handlers. We do not need to dispatch them for ourselves using large p:choose steps.
Since @code on p:catch is optional, this enhancement will not break any XProc 1.0 pipeline.
|
Note
|
Discuss |
The specs also introduce a new element in p:try: Now we can have an optional p:finally which has no input and no output and is intended for cleanup tasks. But there is a note saying:
I’m not actually sure p:finally is worth doing, but I’ve sketched it in for completeness. Also, should p:catch be entirely optional, allowing just try/group/finally?
Speaking for myself: I would like to have such a step for simplicity and safety reasons. I have a lot of pipelines where I lock some datastore and then perform some actions which might fail and are therefore incapsulated in a p:try. In such a scenario I always have to remember to unlock the datastore when things went right and when something went wrong. With p:finally I hope to develop a habit always using the new element, so I do not have to remember to do the unlocking in p:group and all the new p:catch clauses.
Primary output ports on all standard-library steps and option "version" on p:xquery and p:validate-with-xml-schema
Before we will come to the big issue let me just mention to minor changes:
-
According to the development since the original publication of the XProc 1.0 specs there is not just XQuery, but XQuery 3.0 and XQuery 3.1. So the
p:xquerystep obviously was in need of a new option allowing to specify the version to use. The same holds forp:validate-with-xml-schema. Both steps will now throw anerr:XC0038if the requested version is not available. -
XPath 1.0 is no longer supported!
-
In the standard library of XProc 1.0 there were many steps having a single output port which was primary by default, but on some step’s signature @primary was explicitly set to false:
p:compare,p:parameters,p:store,p:xsl-formatter.
|
Important
|
There is still an explicit not primary output port on p:in-scope-names, but I think this is an oversight.
|
|
Note
|
Discuss primary output port on p:store and p:xsl-formatter.
|
While I generally agree with the changes in order to have a consistent syntax of XProc I doubt that it is useful to expose the results of p:store and p:xsl-formatter on a primary output port. I can not remember to have ever used the c:result with the URI of the stored document. And as primary output ports have to be bound to the primary input port of another step, I see myself writing a lot of p:sink steps after my `p:store`s in the future.
But I am open to argumentation here because one big advantage is the ability to end a subpipeline which has a primary output port with a p:store without seeing the error message "err:XS0005. It is a static error if the primary output port of any step is not connected." ever again.
While XProc 1.0 was clearly focused on processing XML documents, XProc 1.1 now introduces a new concept of documents:
A document is a representation and its document properties. […] A representation is a data structure used by an XProc processor to refer to the actual document content.
and
From an XProc perspective, there are two kinds of documents: XML documents and non-XML documents. Non-XML documents can be further subdivided into text documents and binary documents. Text documents are called out specially because they can be easily represented inline within a pipeline.
This is clearly a big change of concept. Let us look at it in some more detail: First the new conception introduces "document properties" as one part of a document. Document properties are key/value pairs holding meta information about the document. Pipeline authors can access this information using the step p:set-properties or using the newly introduced XPath function p:document-properties(). Every document has a property content-type which must always be present. If the document has a base URI, this is represented by the property base-uri. If this property is not defined, the document has no baseURI.
Now let us look at the representation part of the new document. First there is a change in the definition of "XML document": The specs do no longer refer to an XML document as an InfoSet, but as an instance of XQuery 1.0 and XPath 2.0 Data Model (XDM). Because the XDM notion of a document is more liberal than the one entertained by InfoSet, now we can for example have documents with more than one child element. This means now we can unwrap the document element node from a document to add another one like this:
<p:unwrap match="/element">
<p:input port="source">
<element>
<child1/>
<child2/>
</element>
</p:input>
</p:unwrap>
<p:wrap match="/" wrapper="new-element" />Using XDM instead of InfoSet makes document modification more flexible because we do not have to produce a well-formed [InfoSet] document as the output of every step.
Unfortunately this relaxation does not hold, when it comes to the construction of documents with p:inline or the new implicit inline document construction. Suppose I want to add a processing-instruction before the document element node of a document. It might be as easy as this:
<p:insert match="/doc" position="before">
<p:input port="source">
<doc />
</p:input>
<p:input port="insertion">
<p:inline>
<?pi target?>
</p:inline>
</p:input>
</p:insert>But this will raise err:XS0024: "It is a static error if the content of the p:inline element does not consist of exactly one element, optionally preceded and/or followed by any number of processing instructions, comments or whitespace characters." Here the "old" InfoSet conception of a document is still used, irrespective of whether a p:inline is used or not.
|
Note
|
Discuss err:XS0024 for p:inline. Think of XDM documents to be constructed here.
|
In addition to this change in the concept of a document, the input declaration of (atomic) steps now has a new optional attribute "content-types" which holds a blank separated list of content-types accepted by the step on this port. A content-type is specified in the form of "type/subtype+ext". The content-type of a document is one of the document properties. If a document arrives on a port, the processor has to check, whether the actual content-type of this document is compatible with the content-types of the input port declaration. If not, a new error err:XD1003 is raised. As a consequence of the concept of document, the p:data binding used in XProc 1.0 is no longer required. With p:document one can now bind every kind of document, XML or non-XML to a port.
|
Note
|
Discuss "auto-conversion" at least for the case where an XML document is expected by wrapping a c:data around the non-xml document with base64 encoding if necessary.
|
As XProc 1.0 was XML-centric, it should come as no surprise that most of the steps in the standard step library will not accept any non-XML documents: Adding attribute to elements of xml-documents makes sense, but what it means to add an attribute to a text document is at least unclear. The same holds for many other members of the standard step library. But some steps are prepared to consume non-XML documents:
| step name | @content-types | note |
|---|---|---|
|
*/* |
|
|
*/* |
There is an editorial note asking: "Can the input document be JSON?", but it is asked for a single |
|
*/* |
|
|
*/* |
|
|
*/* |
|
|
application/xml */*+xml text/* |
On port "schema", no "application/relax-ng-compact-syntax" ? |
|
application/xml */*+xml text/* |
On port "query". What about "application/xquery"? |
In addition to this known standard library steps there are two new steps specially developed for the new concept of document:
-
p:cast-content-typetransforms the input document from one media type to another. What exactly is done, depends on the content-type of the document on the source port and on the target media type. For XML media types it means just changingcontent-typein the document properties. If a non-xml document should be casted to an XML document, ac:datais wrapped around the representation of the non-XML content. At least forc:datadocuments there is also a way to cast from XML document to non-xml documents. -
p:set-propertiesaugments the document properties with additional information using the content of a map-typed option. Trying to change the property 'content-type' will result in an error raised.
Now this list of steps able to handle non-XML documents is surely not amazing. The full power of the new document concept will unfold with additional steps either in the standard step library or with customer implemented steps.
|
Warning
|
The rest of this paper comments on the state of the discussion before 2017-02-07. Having made great progress, it is conceptional obsolete, interesting only for historical reasons. |
While the new concept of document, comprising XML and non-XML documents likewise, is pretty straightforward and convincing, I think there are still some problems in processing non-XML documents in XProc 1.1. To demonstrate these problems and to present possible solutions, let us look at a use case:
Suppose you have a ZIP-archive which contains different files of different file- or mime-types. Our task is to develop an XProc pipeline which generates a content-report of the items in the ZIP-file. The content report should be an XHTML document, showing for every items its name and its mime-type. Additionally there should be information relative to the mime-type of the file according to the following list:
Mime type |
Content overview |
application/xhtml+xml |
The text of all <h1/> elements in the <body/> |
text/plain |
The first line of the text and the total number of lines. |
text/csv |
The number of cells in the table |
image/jpeg |
The picture’s width and height |
For other mime types no additional information is expected.
Now let us see how to do this in XProc 1.1 and explore whether the set goals could be achieved. First let us imagine a new step for ZIP-Archives, which returns the entries in the archive as a sequence of documents on the result port. The signature of this step might be:
<p:declare-step type="zip:extract">
<p:input port="zip-archive" content-types="application/zip" />
<p:output port="content" sequence="true" />
<p:option name="exclude-system-files" as="xs:boolean" select="true()" />
</p:declare-step>Such a step would be called like this:
<zip:extract>
<p:input>
<p:document href="the-uri-of-the-zip" />
</p:input>
</zip-extract>
<!-- or with this sequence using an option to identify the zip-archive-->
<p:load href="{$path-to-zip}" />
<zip:extract />What should appear on the port 'content' of zip:extract is a sequence of documents in the new conception of 'document' introduced by XProc 1.1: A pair of a representation and document properties, where the latter is a
map(xs:string, xs:string). For the key 'content-type' this map will return the media type of the representation and for the key 'base-uri' the documents base URI (if any).
So if we suppose that zip:extract also sets the base-uri property (in some way), we might get the following four documents on port 'content':
| content-type | base-uri | representation |
|---|---|---|
application/xhtml |
…/doc1.xhtml |
an instance of XDM document-node() |
text/plain |
…/doc2.txt |
a representation of the text |
text/csv |
…/doc3.csv |
a representation of the csv |
image/jpeg |
…/doc4.jpeg |
a representation of the image |
Now we might solve the problem in XProc 1.1 this way:
<p:load href="{$path-to-zip}" />
<zip:extract />
<p:for-each>
<p:variable name="content-type" as="xs:string"
select="map:get(p:document-properties(),'content-type')" /> # (1)
<p:variable name="entry-name" as="xs:string"
select="tokenize(map:get(p:document-properties(),'base-uri'),'/')[last()]" />
<p:choose>
<p:when test="$content-type = 'application/xhtml'">
<p:wrap-sequence wrapper="xhtml-document">
<p:input select="/xhtml:html/xhtml:body/xhtml:h1" />
</p:wrap-sequence>
<p:wrap match="/xhtml-document/xhtml:h1" wrapper="header" />
<p:unwrap match="/xhtml-document/header/xhtml:h1" />
</p:when>
<p:when test="$content-type='text/plain'">
<p:variable name="theText" as="xs:string*" select="tokenize(.,'
')" /> # (2)
<p:identity>
<p:input>
<text-document>
<first-line>{$theText[1]}</first-line>
<n-o-l>{count($theText)}</n-o-l>
</text-document>
</p:input>
</p:when>
<p:when test="$content-type='text/csv'">
<p:variable name="theData" as="xs:string" select="." />
<p:identity>
<p:input>
<csv-document>
<number-of-cells>{count(tokenize($theData,','))+
count(tokenize($theData,' ))-1}</number-of-cells>
</csv-document>
</p:input>
</p:identity>
</p:when>
<p:when test="$content-type='image/jpeg'">
<p:variable name="binary" as="xs:base64Binary" select="." /> # (3)
<p:variable name="location" select="bin:find($binary,0,bin:hex('FFC0'))"/>
<p:identity>
<p:input>
<p:inline>
<jpeg-document>
<width>{
bin:unpack-unsigned-integer($binary,$location+5,2,'most-significant-first')
}</width>
<height>{
bin:unpack-unsigned-integer($binary,$location+7,2,'most-significant-first')
}</height>
</jpeg-document>
</p:inline>
</p:input>
</p:identity>
</p:when>
<p:otherwise>
<p:identity>
<p:input>
<unknown-document/>
</p:input>
</p:identity>
</p:otherwise>
</p:choose>
<p:add-attribute match="/*" attribute-name="name" attribute-value="{$entry-name}" />
</p:for-each>
<p:wrap-sequence wrapper="report-root" />
<p:xslt>
<p:input port="stylesheet">
<p:document href="theStylesheet.xsl" />
</p:input>
</p:xslt>Here I assumed the functions bin:find(), bin:hex() and bin:unpack-unsigned-integer() to be available. The rest of the solution obviously makes heavy use of XProc 1.1’s new features such as typed options/variables, text value templates, attribute value templates, implicit inlines and the ability to connect input to a primary input without typing port='portname'.
There are at least three problems with this step, because it does not conform to the current version of XProc 1.1 specs.
-
The function
p:document-properties()used is not defined. Instead the specs declare a functionp:document-properties($doc as document-node()). So, as it is said in the specs, the properties are only accessible for XML documents, but not for any kind of document to appear on a port. -
It is assumed that non-XML documents can appear as context items in XPath expressions. But the specs say: "The result of evaluating an expression when the context node has a non-XML content type is implementation-defined." This is rather vague as it might mean, that the processor may always return an empty sequence in such a case or even throw an error.
-
The examples clearly presumes that a non-XML document with mime type 'text/plain' and 'text/csv' can be casted or converted to an instance of
xs:stringand that mime type 'image/jpeg' can be casted or converted to an instance ofxs:base64Binary. But that is in no way guaranteed in the specs, because: "Representations of text documents are […] implementation-dependent." and "Representations of non-XML documents are […] implementation-dependent."[3]
It seems pretty clear to me, that we have to remove these constraints in order to get specs, that allow interoperable processing of XProc pipelines. If we seriously want expand XProc 1.0 to be able to work with non-XML documents, I think we have to provide an interface, where these kinds of documents can be processed in an XPath context. If we fail to do this, we will not have XProc 1.1 but different flavours of the language behaving very differently in this central aspect.
Of course pipeline authors are free not to use these implementation-defined features, but to convert every non-XML document into an XML document using p:cast-content-type to invoke a reliable processing mode. But then, I think, we have just replaced the XML-centric processing style of XProc 1.0 by another XML-centric style.
What to do about this? [4] First of all I think we have to say, that every kind of document, that can be processed with XProc can also be processed with XPath, used as an expression language in XProc. Do to this, we might restrict the concept of 'document representation'. Instead of
Definition: A representation is a data structure used by an XProc processor to refer to the actual document content.
I would suggest to say:
New definition: A representation is an instance of the XQuery and XPath Data Model 3.1 (XDM) used by an XProc processor to refer to the actual document content.
Since every instance of an XDM type is an item() it can be the context item of an XPath expression. So by restricting the choices to XDM instances, we can easily guarantee XPath accessibility.
This restriction does indeed take away some flexibility from implementers. In making the representation implementation-dependent, there is a lot of room for optimisations say of large text documents (books?) or large image documents. Since they are implementation-dependent the actual representation of a document with a specific content type might also change over time within the life-cycle of an implementation because the actual representation does not even have to be explained by an implementor claiming conformance for her processor.
Now I think this is a good point: If we open XProc up for processing of non-XML documents everyone still wants an efficient processor having a small memory-footprint and being lightning fast. But I think we have to make compromises here. As I pointed out above, leaving the decision to the implementor means having (no standard) way to access the document’s content with XPath. And this price is clearly too high as far as I am concerned. The only thing to do with non-XML documents were to pass them from step to step, provided these steps are designed for the same implementation. So, for me, making the representation accessible as an XDM instance is a must.
On the other hand, there is a lot of flexibility for implementations, what we need is an XDM instance of the representation if and only if this representation is accessed in an XPath expression. If a large image document is never accessed within an XPath expression, implementors are free to choose any optimisation strategy they like. And secondly: XDM does actually not define any data structure which is to be used to store instances of specific types, although it seems to be quite natural to represent an xs:boolean as a Boolean object in JAVA and so on. What XDM actually defines are operators and functions, so it prescribes behaviour, not data structures. This allows an XDM and/or XPath implementation to choose an appropriate data structure for any purpose. For example one might think of an implementation for xs:string which handles short strings and longer texts in different ways, provided they show the behaviour defined by the specs. So there is a lot of room for optimisation, but it is on the level of the XPath engine used by the XProc processor!
Now this is only half the way, because we just said, that every representation of a document must be an instance of item(). But as you will remember, the step discussed above not only presupposes accessibility in XPath expressions, but also assumes, that text-documents can (at least) be converted to instances of xs:string and that the representation of a JPEG can (at least) be converted to xs:base64Binary. As this is quite natural for the use of a typed language like XPath and XProc 1.0, I think we have to secure this by saying, which type of instances of XDM are associated with the different document types in XProc.[5]
Here is my first proposal for the mapping of representations to media types:
| media type | representation in XDM |
|---|---|
XML media type |
document-node() |
text media type |
xs:string |
application/json |
like in fn:parse-json |
??? |
??? |
others, application/octet-stream |
xs:base64Binary |
Then, there is another problem left with my previous pipeline: What about the function p:document-properties() which is needed to access document properties in an XPath context? As it is currently defined in the specs it is only possible to access the document properties of XML-documents, since the function takes a document-node as parameter. And as non-XML documents are no instances of document-node, we will not be able to access their document properties in XPath expressions. I doubt that the parameter does serve any purpose because document properties are only defined for the document that is passed from XProc to the XPath expression as a context node. So p:document-properties(doc('some-document')) will always return an empty map.
I think the easiest solution to make the document properties of any XProc document accessible in XPath is to augment the dynamic context the XPath expression: Every time an XPath expression is evaluated, this property of the dynamic XPath context is set by the XProc processor. If a context item is present, the value of document-properties is set with the document properties of this item. If no item is present, document-properties is set to an empty map. To access the document properties, the function p:document-properties() is used. What needs to be discussed is whether this function is only visible in XPath expressions evaluated by the XProc processor or if it is available in step contexts too.
If I have a sequence mixed of XML-documents and non-XML documents and I use @select on p:input, I will get an err:XD1004. There are good reasons for this provision, because it does not make sense (or has any predictable result) to use "/html/body/h1" on a text or a jpeg document. On the other hand: When it does make no sense to apply the select expression to non-XML documents, we might say that the select expression does only apply to XML documents appearing in the sequence.
This would be very handy in some use cases, for example when I have a mixed sequence and I want to store only portions of the XML documents I might write:
|
Note
|
Discuss err:XD1004. |
<p:for-each>
<p:input select="/html/body/ul" />
<p:store href="{'document'+p:iteration-position()}" />
</p:for-each>The alternative pipelines avoiding the err:XD1004 would be much longer: I would have to split the mixed sequence into two sequences, an XML and a non-XML sequence using `p:document-property()'. Then I have to iterate over the sequence of XML documents using @select and do the storing. Third I would iterate over the non-XML sequence to save them. There are other ways to do this but they are definitely all longer than the one above.
cast as expression has then to be used to access the representation’s content.