2015-04-21 3 views
4

Короче, вот что я хочу сделать:Как редактировать определенные элементы в файле XML с помощью 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 = (++ "!") 

Вопрос: Как редактировать только отдельные элементы без изменения/удаления их корневых элементов? Также обратите внимание, что эта программа не создает файл "bar.xml", поэтому что-то определенно неправильно. Трассировка показывает, что после применения applic стрелка документ состоит из трех элементов a ("foo", "bar" и "baz"; без восклицательных знаков).

+0

Я развею линзы? – AJFarmar

+0

@AJFarmar, я думаю, это должно быть возможно обойтись без объектива. – Mark

ответ

2

Я не претендую на то, чтобы быть хорошим в HXT, я не использовал его много, но я получил то, что вы хотите сделать, чтобы работать с некоторыми экспериментами. Если у кого-то есть больше опыта работы с HXT, вы можете предложить лучшее решение.

я нашел через скользя над HXT wiki в process* функций, как 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 может быть излишним, особенно если вы только опускаясь вниз в структуре, но это, безусловно, работает, и это не будет изменить другие a элементы в разных частях файла.

+0

Он работает! Однако я не могу поверить, что пользователи HXT * должны изобретать новые примитивы *, чтобы работать в таких простых случаях. Я подожду, может быть, кто-то может предложить идиоматическое решение для HXT. Спасибо. – Mark

+0

С другой стороны, очень хорошо, что 'processChildren' не рекурсивно (например,' deep'), так что, возможно, это действительно способ. Думаю, я соглашусь ответить. (Это вопрос, что весь канал '# haskell' не мог ответить (или не хотел ...)). – Mark

+0

@Mark Этот вопрос, который требует времени и/или специализированных знаний и не приносит большой пользы от случайного обсуждения, намного лучше подходит для формата StackOverflow, чем IRC, поэтому слишком много читайте в этом. :) – kqr

Смежные вопросы