如何使用HXT编辑XML文件中的特定元素?
How to edit specific elements in XML file using HXT?
简而言之,这是我想要完成的:
"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 = (++ "!")
问题:如何在没有changing/removing根元素的情况下直接编辑选中的元素?另请注意,此程序不会创建 "bar.xml"
文件,因此肯定有问题。跟踪显示在应用 applic
箭头后,文档由三个 a
元素组成("foo"
、"bar"
和 "baz"
;没有感叹号)。
HXT我不假装很擅长,我用的不多,但是通过一些实验,我得到了你想做的事情。如果有人对 HXT 有更多经验,请随时提供更好的解决方案。
我通过浏览 HXT wiki 发现了 process*
函数,例如 processTopDown
和 processChildren
,以及其他几个函数。这些似乎是真正允许改变发生的原因。现在,我假设您的实际用例更复杂,您可能只想 select 某个级别的元素。我偶然发现的模式是将 processChildren
与 when
的 HXT 版本一起使用,而不是 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
的调用可能都是多余的,特别是如果您只是深入了解结构,但它确实有效并且不会修改不同部分的其他 a
元素的文件。
简而言之,这是我想要完成的:
"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 = (++ "!")
问题:如何在没有changing/removing根元素的情况下直接编辑选中的元素?另请注意,此程序不会创建 "bar.xml"
文件,因此肯定有问题。跟踪显示在应用 applic
箭头后,文档由三个 a
元素组成("foo"
、"bar"
和 "baz"
;没有感叹号)。
HXT我不假装很擅长,我用的不多,但是通过一些实验,我得到了你想做的事情。如果有人对 HXT 有更多经验,请随时提供更好的解决方案。
我通过浏览 HXT wiki 发现了 process*
函数,例如 processTopDown
和 processChildren
,以及其他几个函数。这些似乎是真正允许改变发生的原因。现在,我假设您的实际用例更复杂,您可能只想 select 某个级别的元素。我偶然发现的模式是将 processChildren
与 when
的 HXT 版本一起使用,而不是 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
的调用可能都是多余的,特别是如果您只是深入了解结构,但它确实有效并且不会修改不同部分的其他 a
元素的文件。