# XUpdate processing

XUpdate is a community specification for using an XML vocabulary to express modifications to XML documents. It is essentially an XPath-based XML transformation language, like XSLT. An XUpdate document is an XML document that specifies what changes should be made to another XML document. XUpdate is supported by many XML processing tools - especially in the open source category - and XUpdate is neither a W3C Recommendation nor an ISO or IETF standard. It is just a project of the XML:DB Initiative's XUpdate Working Group, and it never advanced beyond a Working Draft published in September, 2000. It is not very well specified, but it is very convenient and enables a basic level of functionality, so it has enjoyed popularity in a number of implementations.

4Suite's XUpdate implementation, 4XUpdate, consists of a Python API (via the Ft.Xml.XUpdate module) and a command-line script (4xupdate). The APIs involve taking a source document (the XML to be updated) and an XUpdate document (the changes to apply), and either producing a new document or updating the source document in-place. The command line tool can be used, for example, as a patching utility for XML. All of XUpdate (such as it's specified) is currently implemented.

The Python API can be invoked directly on Domlette objects or on InputSources. Here is an example of using the ApplyXUpdate convenience function, which takes InputSources:

    from Ft.Xml.Domlette import PrettyPrint
    from Ft.Xml.InputSource import DefaultFactory
    try:
        from Ft.Xml.XUpdate import ApplyXUpdate
    except ImportError:
        # the function name changed between 1.0a3 and 1.0b1
        from Ft.Xml.XUpdate import ApplyXupdate as ApplyXUpdate

    SOURCE='''<?xml version = "1.0"?>
    <ADDRBOOK xmlns="http://bogus/">
      <ENTRY ID="fr">
        <NAME>fred</NAME>
      </ENTRY>
    </ADDRBOOK>'''

    XU='''<?xml version="1.0"?>
    <xu:modifications version="1.0" xmlns:xu="http://www.xmldb.org/xupdate"
      xmlns:myns="http://bogus/">
      <xu:append select="/myns:ADDRBOOK" child="last()">
        <ENTRY ID="vz">
          <NAME>Vasia Zhugenev</NAME>
        </ENTRY>
      </xu:append>
    </xu:modifications>'''

    src_isrc = DefaultFactory.fromString(SOURCE, "http://test1/")
    xup_isrc = DefaultFactory.fromString(XU, "http://test2/")

    result_dom = ApplyXUpdate(src_isrc, xup_isrc)
    PrettyPrint(result_dom)

    #expected:
    #<?xml version="1.0" encoding="UTF-8"?>
    #<ADDRBOOK xmlns="http://bogus/">
    #  <ENTRY ID="fr">
    #    <NAME>fred</NAME>
    #  </ENTRY>
    #<ENTRY ID="vz">
    #    <NAME>Vasia Zhugenev</NAME>
    #  </ENTRY>
    #</ADDRBOOK>

If you have both the source document and XUpdate document as Domlette nodes already, you can use the XUpdate processor directly:

    # add to the above script...
    from Ft.Xml.Domlette import NonvalidatingReader
    from Ft.Xml.XUpdate import Processor
    src_isrc = DefaultFactory.fromString(SOURCE, "http://test1/")
    xup_isrc = DefaultFactory.fromString(XU, "[http://test2/")
    src_dom = NonvalidatingReader.parse(src_isrc)
    xup_dom = NonvalidatingReader.parse(xup_isrc)
    proc = Processor()
    proc.execute(src_dom, xup_dom)

    # src_dom has been modified in-place
    PrettyPrint(src_dom)

Using the processor directly allows you to set XPath variables, if needed:

    from Ft.Xml import EMPTY_NAMESPACE

    # execute with $x='foo'
    proc.execute(src_dom, xup_dom, {(EMPTY_NAMESPACE, u'x'): u'foo'})

The command-line script works on local files or even URIs, if resolvable, and normally sends the result XML to standard output, although it can also be made to write to a file. See "4xupdate -h" for usage instructions.

## XUpdate and namespaces

In order to show how to use XUpdate to make namespace-aware modifications, The following tasks will be demonstrated:

1. Add a new element in the products namespace, but using no prefix.

2. Add a new element with a prefix and in the products namespace.

3. Add a new element that is not in any namespace.

4. Add a new global attribute in the XHTML namespace.

5. Add a new global attribute in the special XML namespace.

6. Add a new attribute in no namespace.

7. Remove only the `code` element in the XHTML namespace

8. Remove a global attribute

9. Remove an attribute that is not in any namespace

Modification in place can always be simulated with an addition and then a removal. The following code shows how these tasks can be performed in XUpdate.

    <xup:modifications version="1.0"
      xmlns:xup="http://www.xmldb.org/xupdate"
      xmlns:p="http://example.com/product-info"
      xmlns:html="http://www.w3.org/1999/xhtml"
      xmlns:xl="http://www.w3.org/1999/xlink"
    >

      <!-- Task 1 -->
      <xup:append select="/products/p:product[1]">
        <xup:element
          name="launch-date"
          namespace="http://example.com/product-info"/>
      </xup:append>

      <!-- Task 2 -->
      <xup:append select="/products/p:product[1]">
        <xup:element
          name="p:launch-date"
          namespace="http://example.com/product-info"/>
      </xup:append>

      <!-- Can also be accomplished using literal result elements:
      <xup:append select="/products/p:product[1]">
        <p:launch-date/>
      </xup:append>
      -->

      <!-- Task 3 -->
      <xup:append select="/products/p:product[1]">
        <xup:element name="island"/>
      </xup:append>

      <!-- Can also be accomplished using literal result elements:
      <xup:append select="/products/p:product[1]">
        <island/>
      </xup:append>
      -->

      <!-- Task 4 -->
      <xup:append select="/products/p:product/p:description/html:div">
        <xup:attribute name="global"
          namespace="http://www.w3.org/1999/xhtml">spam</xup:attribute>
      </xup:append>

      <!-- Task 5 -->
      <xup:append select="/products/p:product/p:description/html:div">
        <xup:attribute name="xml:lang">en</xup:attribute>
      </xup:append>

      <!-- Task 6 -->
      <xup:append select="/products/p:product/p:description/html:div">
        <xup:attribute name="class">eggs</xup:attribute>
      </xup:append>

      <!-- Task 7 -->
      <xup:remove select="//html:code"/>

      <!-- Task 8 -->
      <xup:remove select="/products/p:product/p:description/html:div/ref/@xl:href"/>

      <!-- Task 9 -->
      <xup:remove select="/products/p:product[1]/@id"/>

    </xup:modifications>

If you're familiar with XSLT, then you'll see the resemblance of XUpdate at first glance. The envelope element for modifications expressed in XUpdate is `xup:modifications`, similar to `xsl:transform` or `xsl:stylesheet`. The namespace declarations on this element assign prefixes for use in the XUpdate script and have no connection to the prefixes used in the document being modified (the source document), even though they happen to be the same. If you want to access elements in a namespace declared as the default in the source document, then just as in XSLT you must declare and use a prefix for the namespace in the XUpdate script.

Each modification request is expressed as an XUpdate instruction. This example demonstrates `xup:append` and `xup:remove`. There are other instructions providing types of modification such as `xup:insert-before` `xup:update` and there are also control constructs such as `xup:if`, which is similar to `xsl:if`. Instructions usually have a `select` attribute containing an XPath expression that specifies the node to be used as a reference for modification. In the case of `xup:append`, `select` specifies a node after which some new XML will be appended. In the case of `xup:remove`, `select` identifies nodes to be removed. When an instruction needs to specify a chunk of XML to be used in the modification it is expressed as the content of the instructions in a similar fashion to XSLT templates. In the case of `xup:append` this template expresses the chunk of XML to be inserted into the document. In order to generate elements and attributes XUpdate provides output instructions such as `xup:element` and `xup:attribute`, which are very similar to their XSLT equivalents. In another idea borrowed from XSLT, XUpdate allows you to create element by placing literal result elements in the templates. If you'd like to get a closer look at XUpdate, the best way is by browsing the very clear examples in the XUpdate Use Cases compiled by Kimbro Staken. The following listing is a Python code that can be used to apply an XUpdate script. It's a simplified version of the code for the 4xupdate command line.

    import sys
    from Ft.Xml import XUpdate
    from Ft.Xml import Domlette, InputSource
    from Ft.Lib import Uri

    # Set up reader objects for parsing the XML files
    reader = Domlette.NonvalidatingReader
    xureader = XUpdate.Reader()

    # Parse the source file
    source_uri = Uri.OsPathToUri(sys.argv[1], attemptAbsolute=1)
    source = reader.parseUri(source_uri)

    # Parse the XUpdate file
    xupdate_uri = Uri.OsPathToUri(sys.argv[2], attemptAbsolute=1)
    isrc = InputSource.DefaultFactory.fromUri(xupdate_uri)
    xupdate = xureader.fromSrc(isrc)

    # Set up the XUpdate processor and run against the source file
    # The Domlette for the source is modified in place
    processor = XUpdate.Processor()
    processor.execute(source, xupdate)

    # Print the updated DOM node to standard output
    Domlette.Print(source)

Notice the use of `Uri.OsPathToUri` to convert file system paths to proper URIs for use in 4Suite. I strongly recommend this convention as one way to help minimize confusion between file specifications and URIs -- the basis of many frequently asked questions. The `XUpdate.Processor` class defines the environment for running XUpdate commands and `execute()` is the method that actually kicks off the processing. It operates on a Domlette instance, modifying it in place (so be careful when using using XUpdate in this way). The updated document object is printed to standard output using `Domlette.Print`.

The following snippet illustrates how to run the test script, and the output result.

    $ python listing4.py products.xml listing3.xup
    <?xml version="1.0" encoding="UTF-8"?>
    <products xmlns:p="http://example.com/product-info"
    xmlns:html="http://www.w3.org/1999/xhtml"
    xmlns:xl="http://www.w3.org/1999/xlink"
    >
      <product xmlns="http://example.com/product-info">
        <name xml:lang="en">Python Perfect IDE</name>
        <description>
          Uses mind-reading technology to anticipate and accommodate
          all user needs in Python development.  Implements all
           features though
          the year 3000.  Works well with <code>1166</code>.
        </description>
      <launch-date/><p:launch-date/><island/></product>
      <p:product id="1166">
        <p:name>XSLT Perfect IDE</p:name>
        <p:description>
          <p:code>red</p:code>
          <html:code>blue</html:code>
          <html:div global="spam" class="eggs" xml:lang="en">
            <ref xl:type="simple">A link</ref>
          </html:div>
        </p:description>
      </p:product>
    </products>