Splitter advice

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Splitter advice

Arjan Moraal
Hi all,

Take the following XML:
  <cats>
    <cat name="Ginger"><colour id="1"/></cat>
    <cat name="Mr Boots"><colour id="3"/></cat>
    <cat name="Delphi"><colour id="1"/></cat>
  </cats>

Is it possible to use the Splitter to break split up this XML, but grouped by colour id? So it should result in two messages:

  <cats>
    <cat name="Ginger"><colour id="1"/></cat>
    <cat name="Delphi"><colour id="1"/></cat>
  </cats>

and

  <cats>
    <cat name="Mr Boots"><colour id="3"/></cat>
  </cats>

Without the surrounding <cats> tags would be fine as well since I could add these manually using setBody().

So far, I have set up this TestCase, but the problem is it breaks the xml up into 3 messages instead of 2.

    public void testGroupedXMLSplitter() throws Exception {
        log.debug("testGroupedXMLSplitter()");
       
        final String xml = "<cats>" +
                        "<cat name=\"Ginger\"><colour id=\"1\"/></cat>" +
                        "<cat name=\"Mr Boots\"><colour id=\"3\"/></cat>" +
                        "<cat name=\"Delphi\"><colour id=\"1\"/></cat>" +
                     "</cats>";
       
        context.addRoutes(
                new RouteBuilder() {
                    @Override
                    public void configure() {
                        from("direct:cats")
                            .splitter(xpath("/cats/cat/colour/@id"))
                            .to("mock:result");
                    }
                }
            );

            resultEndpoint.expectedMessageCount(2);
           
            template.send("direct:cats", new Processor() {
                public void process(Exchange exchange) {
                    Message in = exchange.getIn();
                    in.setBody(xml);
                }
            });
           
            resultEndpoint.assertIsSatisfied();
    }

Any help would be appreciated very much!

Thanks,
Arjan
Reply | Threaded
Open this post in threaded view
|

Re: Splitter advice

jstrachan
I guess this is a great use case for XQuery, where you can use the
kinda SQL-ish nature to iterate through all values of the colour id,
then for each of them create a kinda nested document; then split that
into pieces. (You could use XSLT as well).

So transform into something like

<colours>
 <colour id="1"> <cat/> <cat/> </colour>

Then split on /colours/colour

On 26/10/2007, Arjan Moraal <[hidden email]> wrote:

>
> Hi all,
>
> Take the following XML:
>   <cats>
>     <cat name="Ginger"><colour id="1"/></cat>
>     <cat name="Mr Boots"><colour id="3"/></cat>
>     <cat name="Delphi"><colour id="1"/></cat>
>   </cats>
>
> Is it possible to use the Splitter to break split up this XML, but grouped
> by colour id? So it should result in two messages:
>
>   <cats>
>     <cat name="Ginger"><colour id="1"/></cat>
>     <cat name="Delphi"><colour id="1"/></cat>
>   </cats>
>
> and
>
>   <cats>
>     <cat name="Mr Boots"><colour id="3"/></cat>
>   </cats>
>
> Without the surrounding <cats> tags would be fine as well since I could add
> these manually using setBody().
>
> So far, I have set up this TestCase, but the problem is it breaks the xml up
> into 3 messages instead of 2.
>
>     public void testGroupedXMLSplitter() throws Exception {
>         log.debug("testGroupedXMLSplitter()");
>
>         final String xml = "<cats>" +
>                         "<cat name=\"Ginger\"><colour id=\"1\"/></cat>" +
>                         "<cat name=\"Mr Boots\"><colour id=\"3\"/></cat>" +
>                         "<cat name=\"Delphi\"><colour id=\"1\"/></cat>" +
>                      "</cats>";
>
>         context.addRoutes(
>                 new RouteBuilder() {
>                     @Override
>                     public void configure() {
>                         from("direct:cats")
>                             .splitter(xpath("/cats/cat/colour/@id"))
>                             .to("mock:result");
>                     }
>                 }
>             );
>
>             resultEndpoint.expectedMessageCount(2);
>
>             template.send("direct:cats", new Processor() {
>                 public void process(Exchange exchange) {
>                     Message in = exchange.getIn();
>                     in.setBody(xml);
>                 }
>             });
>
>             resultEndpoint.assertIsSatisfied();
>     }
>
> Any help would be appreciated very much!
>
> Thanks,
> Arjan
>
>
> --
> View this message in context: http://www.nabble.com/Splitter-advice-tf4696876s22882.html#a13425803
> Sent from the Camel - Users mailing list archive at Nabble.com.
>
>


--
James
-------
http://macstrac.blogspot.com/

Open Source SOA
http://open.iona.com
Reply | Threaded
Open this post in threaded view
|

Re: Splitter advice

Arjan Moraal
Thanks James.

Not sure if I'm ready for XQuery yet. I think I was hoping for a one-line solution ;)

But I now managed to do it with a custom expression:

        context.addRoutes(
                new RouteBuilder() {
                    @Override
                    public void configure() {
                        from("direct:cats")
                        .splitter(new Expression<Exchange>() {
                            public Object evaluate(Exchange exchange) {
                                log.debug("evaluate()");
                                HashMap<String,String> snippets = new HashMap<String,String>();
                                org.dom4j.Document xmlDoc =
                                    (new DOMReader()).read(exchange.getIn().getBody(
                                            org.w3c.dom.Document.class));                                
                                Iterator<Node> it = xmlDoc.selectNodes("cats/cat").iterator();
                                while (it.hasNext()) {
                                    Node child = it.next();
                                    String key = child.valueOf("colour/@id");
                                    if (snippets.containsKey(key)) {
                                        snippets.put(key, snippets.get(key)+child.asXML());
                                    } else {
                                        snippets.put(key, child.asXML());
                                    }
                                }
                                return snippets.values();
                            }
                          })
                        .setBody(el("<cats>${in.body}</cats>"))
                        .to("mock:result");
                    }
                }
            );


James.Strachan wrote
I guess this is a great use case for XQuery, where you can use the
kinda SQL-ish nature to iterate through all values of the colour id,
then for each of them create a kinda nested document; then split that
into pieces. (You could use XSLT as well).

So transform into something like

<colours>
 <colour id="1"> <cat/> <cat/> </colour>

Then split on /colours/colour
Reply | Threaded
Open this post in threaded view
|

Re: Splitter advice

jstrachan
On 26/10/2007, Arjan Moraal <[hidden email]> wrote:
>
> Thanks James.
>
> Not sure if I'm ready for XQuery yet. I think I was hoping for a one-line
> solution ;)

Yeah - me too :) Well with enough XQuery knowledge, it would be about
a one liner :). I confess I'm no XQuery ninja either. Something
vaguely like...

for $x in distinct-values(//colour/@id) return //cat[colour/@id = $x]

Though warning I've never done much with XQuery so the above could be
total garbage :)



> But I now managed to do it with a custom expression:

[snip]

Yeah - its nice that folks can always drop down to good old Java code
and solve problems that way; then over time hopefully we'll be able to
solve these kinds of issues in more elegant ways.

--
James
-------
http://macstrac.blogspot.com/

Open Source SOA
http://open.iona.com