Groovy使用StreamingMarkupBuilder向闭包传参生成XML的实现问题
问题说明
运行环境:Groovy 2.4, Spring 5.3.13
使用StreamingMarkupBuilder生成XML序列化输出未达预期,原有代码框架如下:
public void createMsgToStreamOut( String strCreatedAt, String strEntity, String strIdNum, String strEvent) { def streamBuilder = new StreamingMarkupBuilder(); streamBuilder.encoding = "UTF-8" def xml = streamBuilder.bind{ strCreatedAt, strEntity, strIdNum, strEvent -> >> some magic goes here } def xmlStr = XmlUtil.serialize( xml) println xmlStr; } createMsgToStreamOut( "2022-09-10T12:13:14.567", "Matter", "907856", "create");
预期输出XML结构:
<?xml version="1.0" encoding="UTF-8"?> <message> <timestamp>2022-09-10T12:13:14.567</timestamp> <entity>Matter</entity> <number>907856</number> <event>create</event> </message>
需求为实现bind闭包参数传入、符合要求的XML生成逻辑,同时支持后续输出流对接Kafka生产者。
实现方案
原有代码核心错误:StreamingMarkupBuilder的bind方法接收的闭包无需在参数列表重复声明外层方法入参,重复声明会导致参数被识别为闭包内部未赋值的绑定变量,无法获取实际传入值。闭包可直接访问外层作用域的方法参数。
基础生成打印实现
import groovy.xml.StreamingMarkupBuilder import groovy.xml.XmlUtil void createMsgToStreamOut(String strCreatedAt, String strEntity, String strIdNum, String strEvent) { def streamBuilder = new StreamingMarkupBuilder() streamBuilder.encoding = "UTF-8" def xml = streamBuilder.bind { message { timestamp(strCreatedAt) entity(strEntity) number(strIdNum) event(strEvent) } } def xmlStr = XmlUtil.serialize(xml) println xmlStr } // 测试调用 createMsgToStreamOut("2022-09-10T12:13:14.567", "Matter", "907856", "create")
运行后可直接输出符合预期格式的XML内容。
对接Kafka流式实现
无需先生成完整字符串再发送,直接对接输出流可降低内存占用:
import groovy.xml.StreamingMarkupBuilder import org.apache.kafka.clients.producer.KafkaProducer import org.apache.kafka.clients.producer.ProducerRecord import java.io.ByteArrayOutputStream void sendMsgToKafka(String strCreatedAt, String strEntity, String strIdNum, String strEvent, KafkaProducer<String, byte[]> kafkaProducer, String kafkaTopic) { def streamBuilder = new StreamingMarkupBuilder() streamBuilder.encoding = "UTF-8" def xmlClosure = { message { timestamp(strCreatedAt) entity(strEntity) number(strIdNum) event(strEvent) } } // 直接写入输出流,跳过中间字符串生成 ByteArrayOutputStream outputStream = new ByteArrayOutputStream() outputStream.withWriter("UTF-8") { writer -> writer << streamBuilder.bind(xmlClosure) } // 组装消息发送到Kafka ProducerRecord<String, byte[]> record = new ProducerRecord<>(kafkaTopic, outputStream.toByteArray()) kafkaProducer.send(record) }
注意事项
- 若需显式给bind闭包传参,可通过闭包Binding对象设置变量后传入,当前场景直接访问外层方法参数是最简洁的实现
- Groovy 2.4版本下
XmlUtil.serialize会自动按照设置的编码生成XML声明头,无需额外配置 - 高性能场景下可直接将Writer对接Kafka的字节缓冲区,避免ByteArrayOutputStream的额外内存拷贝
内容的提问来源于stack exchange,提问作者code_warrior
相关产品推荐
相关产品推荐

