使用 xslt 聚合 [英] Aggregate with xslt
问题描述
我正在尝试从数据库获取数据后聚合消息.好像很难解释,先画个流程.
I am trying to aggregate message after getting data from database. It seems to be hard to explain to I will draw a flow firstly.
[ORIGINAL_DOCUMENT] --> SPLIT[GET SOME IDs from ORIGINAL_DOCUMENT] --> [GET DATA FROM DATABASE USING MYBATIS] --> [ENRICH ORIGINAL_DOCUMENT BY GOT DATA FROM DATABASE]
第一条路线:
<route id="enrich-zamowienie">
<from uri="activemq:queue:original-document"/>
<setHeader headerName="pure-xml">
<simple>${body}</simple>
</setHeader>
<split>
<xpath>original-document/entry</xpath>
<unmarshal>
<jaxb contextPath="com.original-document"/>
</unmarshal>
<setBody>
<simple>${body.getEntryId()}</simple>
</setBody>
<to uri="activemq:queue:getAdditionalsByID" />
<marshal>
<jaxb contextPath="com.additionals"
encoding="utf-8" prettyPrint="true"/>
</marshal>
<setHeader headerName="entry">
<simple>${body}</simple>
</setHeader>
<setBody>
<simple>${header.pure-xml}</simple>
</setBody>
<to uri="direct:aggregate" />
</split>
</route>
第二条路线:
<route>
<from uri="direct:aggregate" />
<aggregate strategyRef="aggregator">
<correlationExpression>
<xpath>?</xpath>
</correlationExpression>
</aggregate>
<log message="${body}" />
</route>
(...)
<bean id="aggregator" class="org.apache.camel.util.toolbox.XsltAggregationStrategy">
<constructor-arg value="com/transformXSLT.xsl" />
我从 actimvemq 得到的原始 xml:
my original xml that I get fromy actimvemq:
<document>
<header>
<header_id>1</header_id>
</header>
<body>
<entry>
<entryId>1</entryId>
<fieldToEnrich1></fieldToEnrich1>
<fieldToEnrich2></fieldToEnrich2>
<fieldToEnrich3></fieldToEnrich3>
</entry>
<entry>
<entryId>2</entryId>
<fieldToEnrich1></fieldToEnrich1>
<fieldToEnrich2></fieldToEnrich2>
<fieldToEnrich3></fieldToEnrich3>
</entry>
<entry>
<entryId>3</entryId>
<fieldToEnrich1></fieldToEnrich1>
<fieldToEnrich2></fieldToEnrich2>
<fieldToEnrich3></fieldToEnrich3>
</entry>
</body>
</document>
当然,每个 id 的附加值看起来都像这样:
And of coure for every id addtional looks like it:
<document>
<additionals>
<fieldToEnrich1>131</fieldToEnrich1>
<fieldToEnrich2>3232</fieldToEnrich2>
<fieldToEnrich3>3213</fieldToEnrich3>
</additionals>
</document>
我的目标是使用数据库中的额外数据创建类似 original_document 的文档.我不知道第二条路线应该看起来如何.我希望这是可以理解的.
My aim is to create document like original_document by with extra data from database. I don't know how to second route should looks. I hope it is understandable.
推荐答案
@pcoates 有使用 XsltAggregationStrategy 的好主意.因此,我使用 xslt 模板和修改后的策略为您制作了工作示例.路线:
@pcoates have good idea to use XsltAggregationStrategy. So i make working example for you with xslt templates and modified strategy. Route:
XsltAggregationStrategy aggregationStrategy = new CustomXsltAggregationStrategy("xslt/aggregate.xsl");
from("timer://foo?period=30s")
.setBody(constant("<document>\n" +
" <header>\n" +
" <header_id>1</header_id>\n" +
" </header>\n" +
" <body>\n" +
" <entry>\n" +
" <entryId>1</entryId>\n" +
" <fieldToEnrich1></fieldToEnrich1>\n" +
" <fieldToEnrich2></fieldToEnrich2>\n" +
" <fieldToEnrich3></fieldToEnrich3>\n" +
" </entry>\n" +
" <entry>\n" +
" <entryId>2</entryId>\n" +
" <fieldToEnrich1></fieldToEnrich1>\n" +
" <fieldToEnrich2></fieldToEnrich2>\n" +
" <fieldToEnrich3></fieldToEnrich3>\n" +
" </entry>\n" +
" <entry>\n" +
" <entryId>3</entryId>\n" +
" <fieldToEnrich1></fieldToEnrich1>\n" +
" <fieldToEnrich2></fieldToEnrich2>\n" +
" <fieldToEnrich3></fieldToEnrich3>\n" +
" </entry>\n" +
" </body>\n" +
"</document>"))
.convertBodyTo(Document.class)
.setProperty("updated-xml", simple("body"))
.split().xpath("//entry").aggregationStrategy(aggregationStrategy)
.setHeader("key", xpath("//entryId/text()", String.class))
.setProperty("update-node", simple("body"))
// <to uri="activemq:queue:getAdditionalsByID" />
// like you receive your data
.process(exchange -> {
String data = "enrich data for key:"+exchange.getIn().getHeader("key");
exchange.getIn().setBody(String.format("<document><additionals><fieldToEnrich1>%s</fieldToEnrich1><fieldToEnrich2>%s</fieldToEnrich2><fieldToEnrich3>%s</fieldToEnrich3></additionals></document>",
data,data,data));
})
.convertBodyTo(Document.class)
.setProperty("additional", simple("body"))
.setBody(exchangeProperty("update-node"))
.to("xslt:xslt/updateNode.xsl")//create modified node
.end()
.convertBodyTo(String.class)
.log(LoggingLevel.INFO, "Body:${body}");
updateNode.xsl:
updateNode.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:strip-space elements='*'/>
<xsl:param name="additional"/>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="entry">
<xsl:copy>
<xsl:apply-templates select="@*|node()[name()='entryId']"/>
<xsl:apply-templates select="$additional/document/additionals/*"/>
</xsl:copy>
</xsl:template>
aggregate.xsl:
aggregate.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt">
<xsl:output method="xml" version="1.0" encoding="UTF-8"/>
<xsl:strip-space elements='*'/>
<xsl:param name="new-exchange"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//entry">
<xsl:choose>
<xsl:when test="./entryId/text()=$new-exchange/entry/entryId/text()">
<xsl:copy-of select="$new-exchange"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
CustomXsltAggregationStrategy 类:
CustomXsltAggregationStrategy class :
public class CustomXsltAggregationStrategy extends XsltAggregationStrategy {
/**
* Constructor.
*
* @param xslFileLocation location of the XSL transformation
*/
public CustomXsltAggregationStrategy(String xslFileLocation) {
super(xslFileLocation);
}
@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
//by default result body of first split iteration become document that will be transformed by another iterations
//but we need original document to be transformable, so we make pseudo first body and setting there our original document
//others iteration will change only our document
if (oldExchange == null) {
oldExchange = newExchange.copy();
oldExchange.getIn().setBody(oldExchange.getProperty("updated-xml"));
}
return super.aggregate(oldExchange, newExchange);
}
输出:
2018-12-13 14:32:24,643 | INFO | 62 - timer://foo | route82 | 98 - org.apache.camel.camel-core - 2.16.3 | Body:<?xml version="1.0" encoding="UTF-8"?><document><header><header_id>1</header_id></header><body><entry><entryId>1</entryId><fieldToEnrich1>enrich data for key:1</fieldToEnrich1><fieldToEnrich2>enrich data for key:1</fieldToEnrich2><fieldToEnrich3>enrich data for key:1</fieldToEnrich3></entry><entry><entryId>2</entryId><fieldToEnrich1>enrich data for key:2</fieldToEnrich1><fieldToEnrich2>enrich data for key:2</fieldToEnrich2><fieldToEnrich3>enrich data for key:2</fieldToEnrich3></entry><entry><entryId>3</entryId><fieldToEnrich1>enrich data for key:3</fieldToEnrich1><fieldToEnrich2>enrich data for key:3</fieldToEnrich2><fieldToEnrich3>enrich data for key:3</fieldToEnrich3></entry></body></document>
还有一点建议,如果您需要保存临时正文或一些计算数据,请使用属性而不是标头,因为标头可以由端点处理.
And a little advice, if you need to save temporary body or some computed data, use properties instead of headers, cause headers can be processed by endpoints.
这篇关于使用 xslt 聚合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!