[Haskell-cafe] How do I insert an element in HaXml?

Jeremy Shaw jeremy.shaw at linspireinc.com
Mon May 21 14:31:56 EDT 2007


At Sun, 20 May 2007 10:55:26 +0100,
Malcolm Wallace wrote:
> 
> "Jeremy Shaw" <jeremy.shaw at linspireinc.com> 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>
  <othersection attr=\"value2\"/>
</top>

And a I want to add single <newElement /> to any child of <top> that
already has one or more <b /> element, so I get this output:

<top>
  <section attr="value1">
    <newElement />
    <b />
    <b />
  </section>
  <section2 attr="value3">
    <newElement />
    <b />
  </section2>
  <othersection attr="value2"/>
</top>

A solution that comes close is this filter:

> 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.


More information about the Haskell-Cafe mailing list