简而言之,这就是我想要实现的目标:
“foo.xml”:
<?xml version="1.0"?>
<foo>
<bar>
<baz>
<a>foo</a>
<a>bar</a>
<a>baz</a>
</baz>
</bar>
</foo>
预期结果(“bar.xml”的内容):
<?xml version="1.0"?>
<foo>
<bar>
<baz>
<a>foo!</a>
<a>bar!</a>
<a>baz!</a>
</baz>
</bar>
</foo>
……我试图解决这个问题:
module Main (main) where
import Control.Monad
import Control.Arrow.ArrowTree
import Text.XML.HXT.Core
main :: IO ()
main = void . runX $readDocument [] "foo.xml" >>>
applic >>> writeDocument [withIndent yes] "bar.xml"
applic :: IOSArrow XmlTree XmlTree
applic = getChildren >>> hasName "foo"
-- ^^ because of extra root node (?)
/> hasName "bar" /> hasName "baz" /> hasName "a" >>> changeText excl
excl :: String -> String
excl = (++ "!")
问题:如何在不更改/删除其根元素的情况下直接编辑所选元素?另请注意,此程序不会创建“bar.xml”文件,因此肯定是错误的.跟踪显示在应用应用箭头后,文档由三个元素组成(“foo”,“bar”和“baz”;没有感叹号).
最佳答案 我并没有假装擅长HXT,我没有太多使用它,但是我已经通过一些实验得到了你想要做的事情.如果有人对HXT有更多经验,请随时提供更好的解决方案.
我发现通过浏览HXT wiki进程*函数,如processTopDown和processChildren,以及其他几个.这些似乎实际上允许改变发生.现在,我假设您的实际用例更复杂,您可能只想选择某个级别的元素.我偶然发现的模式是使用processChildren和HXT版本的when,而不是Control.Monad,因为它们不一样.基本上,我的第一个实现是
applic
= processChildren
$flip when (isElem >>> hasName "foo")
$processChildren
$flip when (isElem >>> hasName "bar")
$processChildren
$flip when (isElem >>> hasName "baz")
$processChildren
$flip when (isElem >>> hasName "a")
$processChildren
$flip when isText
$changeText excl
这对我来说真的很难看,重复的方式太多了.因此我将其抽象为更具可读性的东西:
-- Fixity is important here, must be right-associative.
infixr 5 />/
(/>/) :: ArrowXml a => String -> a XmlTree XmlTree -> a XmlTree XmlTree
name />/ action
= processChildren
$action `when` (isElem >>> hasName name)
applic = "foo" />/ "bar" />/ "baz" />/ "a" />/
processChildren (
changeText excl `when` isText
)
所有这些对processChildren的调用可能都是多余的,特别是如果你只是深入到结构中,但它确实有效,它不会修改文件不同部分的其他元素.