
Hello, How do I create a HaXml filter that adds a new element as a child of an existing element. For example, let's say I have the XML document: <a> <b/> <c/> </a> How do I add a new element under <a /> so that I have the document: <a> <newElement/> <b/> <c/> </a> The following works, but it seems very hackish because I have to use the underlying HaXml data types. I think I could possibly use the filters in Text.XML.HaXml.Xtract.Combinators, but that seems like overkill for such a seemingly simple (and common?) operation. import Text.XML.HaXml import Text.XML.HaXml.Types import Text.XML.HaXml.Pretty insertNewElement = extract (\(CElem (Elem name _as cs)) -> mkElem name [mkElem "newElement" [] , const cs]) where extract :: (Content -> CFilter) -> CFilter extract f = \c -> f c undefined -- these are just helper functions to invoke my filter. But, I'll -- happily accept improvements to these functions as well ;) main = print $ document $ runFilter "<a><b /><c /></a>" insertNewElement runFilter :: String -> CFilter -> Document runFilter xmlStr cfilter = let (Document p s e m) = xmlParse "" xmlStr in case (cfilter (CElem e)) of [CElem e'] -> Document p s e' m [] -> error "produced no output" _ -> error "produced more than one output" thanks! j.

"Jeremy Shaw"
How do I create a HaXml filter that adds a new element as a child of an existing element. For example, let's say I have the XML document: <a> <b/> <c/> </a> How do I add a new element under <a /> so that I have the document: <a> <newElement/> <b/> <c/> </a>
Using the combinators, it goes something like this: mkElem "a" [ mkElem "newElement" [] , children ] `o` tag "a" Having matched the outer tag, you then rebuild it with some extra stuff added. Obviously you would abstract the common pattern if you do this frequently: insertAtStart :: String -> String -> CFilter insertAtStart outer newelem = mkElem outer [ mkElem newElem [] , children ] `o` tag outer Regards, Malcolm

At Sun, 20 May 2007 10:55:26 +0100, Malcolm Wallace wrote:
"Jeremy Shaw"
writes: How do I create a HaXml filter that adds a new element as a child of an existing element. For example, let's say I have the XML document: <a> <b/> <c/> </a> How do I add a new element under <a /> so that I have the document: <a> <newElement/> <b/> <c/> </a>
Using the combinators, it goes something like this:
mkElem "a" [ mkElem "newElement" [] , children ] `o` tag "a"
Having matched the outer tag, you then rebuild it with some extra stuff added. Obviously you would abstract the common pattern if you do this frequently:
insertAtStart :: String -> String -> CFilter insertAtStart outer newelem = mkElem outer [ mkElem newElem [] , children ] `o` tag outer
Thanks! I should probably mention that I don't actually have a
specific problem I am trying to solve here. I am trying to write a
tutorial on how to use the combinators, and there is a certain class
of problems I can't seem to handle. I am not sure if it is because
there is a hole in my understanding, or if there is an hole in what
the combinators can express. The hole is related to operations that
want to edit the children of a element in some sort of aggregate
manner. The current example is inserting a new element. Another
example is sorting the children.
Going back to the example at hand, what if I don't know the name and
attributes of the parent? For example, if I have the document:
<top>
<section attr="value1">
<b />
<b />
</section>
<section2 attr="value3">
<b />
</section2>
chip ((chip (mkElem "newElement" [] `union` keep)) `when` (elm /> tag "b"))
Except that it will produce a <newElement /> for *every* <b />, whereas I only want a single <newElement /> per parent. If I introduce a new (poorly named) primitive:
editElem :: CFilter -> CFilter editElem f c@(CElem (Elem name as _)) = [ CElem (Elem name as (f c)) ]
Then I can express the filter like this:
chip (editElem (mkElem "newElement" [] `union` children) `when` (keep /> tag "b"))
But, I don't see an obvious way to implement 'editElem' using the existing combinators. On the other hand, 'chip' can be implemented in terms of 'editElem':
chip' = editElem children
but, I am not sure what implications that has, if any. thanks! j.

"Jeremy Shaw"
Another example is sorting the children.
sortedChildren match = mkElem match [ sortBy ((<=) `on` tagName) . children ] `o` tag match where tagName (CElem (Elem name _ _ _)) = name or maybe, to use the labelling combinators rather than the underlying representation: sortedChildren match = mkElem match [ const keep `oo` sortBy ((<=) `on` fst) . tagged children ] `o` tag match Of course, one outstanding problem is that the mkElem e [ ... ] `o` tag e trick does not preserve attributes from the matched element.
Then I can express the filter like this: chip (editElem (mkElem "newElement" [] `union` children) `when` (keep /> tag "b"))
But, I don't see an obvious way to implement 'editElem' using the existing combinators.
Yes, this is definitely difficult without 'editElem'. Not only do you need to preserve the element's attributes, but also its tag name. It would be nice to be able to bind some result of a combinator expression to a variable name, then use the bound name elsewhere in the expression, but unfortunately this is not how things work in HaXml. Maybe if CFilters were monadic? ... it is an idea I have played with but never settled on.
I am not sure if it is because there is a hole in my understanding, or if there is an hole in what the combinators can express.
I am sympathetic to the idea that there could be an unfilled hole in the design space, in particular that there may be simpler primitive combinators from which to build more powerful and comprehensive editing facilities.
If I introduce a new (poorly named) primitive:
editElem :: CFilter -> CFilter editElem f c@(CElem (Elem name as _)) = [ CElem (Elem name as (f c)) ]
This is remarkably similar to the definition of 'chip', except that it applies the filter to the element itself, rather than to its children. Hence, a name suggestion based on this relationship could be 'inplace'. It definitely looks useful certainly more primitive than 'chip', and I'll add it to HaXml, unless you can think of an even better combinator!
On the other hand, 'chip' can be implemented in terms of 'editElem':
chip' = editElem children
Strictly speaking, it would be chip' f = editElem (f `o` children) You can't eta-reduce `o` as if it were normal composition! Regards, Malcolm
participants (2)
-
Jeremy Shaw
-
Malcolm Wallace