1

I need to deep select a XML node from a document and transform it then move it to another position.

This example is taken from Haskell cafe and slightly changed:

Select the //d/e/f and then change the content at f and move the new f node under a.

<a>
...
  <d>
    <e>
      <f>some data to change
      </f>
    </e>
  </d>
...
</a> 

I need to do this kind of operation for several times, so it's better if the update operation is composeable.

I am looking at xml-conduit package, but seems it lacks of DOM manipulation functions, what's the best way to do this kind of task?

Sawyer
  • 15,581
  • 27
  • 88
  • 124
  • 3
    What do you mean by DOM manipulation? The data structures used by xml-conduit- like most data structures in Haskell- are immutable. The way you transform an XML tree is by creating a new tree. If you describe in more detail what you're trying to achieve, you may get more informative answers. – Michael Snoyman Mar 17 '14 at 06:57
  • @MichaelSnoyman Thanks Michael, I've updated the question, and I've read your solution to the problem, but hard code the selection logic into recursion is not what I want, it would be nice if xml-conduit has `update` or `insertAt` functions. – Sawyer Mar 17 '14 at 07:29
  • @MichaelSnoyman also, seems xml-conduit is implemented using Zipper, it's very easy to select f `cur $// element "d" $/ element "e" $/ element "f"`, but if I made some changes and want to go back to the original tree with the changes, what should I do? The zipper hole and zipper context form the whole tree, it should have a way to go back right? I used to use this in Anti-XML, see here: http://anti-xml.org/zippers.html starts from the `Now comes the tricky part` paragraph. – Sawyer Mar 17 '14 at 07:52
  • That usage isn't really encouraged by the xml-conduit API. However, it looks like exactly the kind of usage that a lens wrapper would support. I haven't used it myself, but I'd recommend having a look at [xml-lens](https://hackage.haskell.org/package/xml-lens). – Michael Snoyman Mar 17 '14 at 14:59
  • @MichaelSnoyman `xml-lens` seems like a solely lens based XML selector, doesn't do the transformation, and the `xml-conduit` axes can also do the selection work. I feel like the `xml-conduit` only has the Zipper navigation functions, plus some filters, but doesn't have merging context and focus back the original tree features, which are also useful. – Sawyer Mar 17 '14 at 15:18
  • possible duplicate of [How do i replace Nodes in HXT?](http://stackoverflow.com/questions/9483248/how-do-i-replace-nodes-in-hxt) – Paul Sweatte Sep 05 '14 at 01:21

1 Answers1

1

I'm quite new to HXT, so there may be better ways, but I solved a similar problem like this:

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE Arrows, NoMonomorphismRestriction #-}

module Abc where

import Text.XML.HXT.Core

moveElts :: ArrowXml a => a XmlTree XmlTree
moveElts =
  processTopDown (
    -- Insert <f>'s under <a>:
    slurpF `when` hasName "a"
    >>>
    -- and remove the old <f>'s:
    removeChildrenExcept "f" `when` neg (hasName "a")
    )
  where
    removeChildrenExcept tag =
      replaceChildren (getChildren >>> neg (hasName tag))
    slurpF = proc a -> do
      elts <- deep (hasName "f") -< a
      returnA (insertChildrenAt 0 (constA elts)) -<< a


-- Test:
test :: IO ()
test = runX (readDocument [withValidate no] "abc.xml"
             >>> moveElts
             >>> writeDocumentToString [withIndent yes])
       >>= putStrLn . head
unhammer
  • 4,306
  • 2
  • 39
  • 52