BizTalk发送含动态GUID与数字属性名的JSON方案咨询
我之前在BizTalk项目里碰到过几乎一模一样的动态JSON生成问题,给你几个实际验证过的最优解决方案,帮你优雅搞定这个场景:
最优方案1:采用带属性的规范XML结构(首推)
这是最清晰、最易于维护的方案,核心思路是把原本无效的动态节点名(数字、GUID)转成节点属性,让XML完全符合规范。
第一步:调整XML结构为有效格式
把你提供的无效XML改成如下结构,用name属性存储原本的动态键名:
<root> <element> <anotherGuid>05060c4c-f0af-46b8-810e-30c0c00a379e</anotherGuid> <attr> <dynamicEntry name="b587d548-8aa6-42b7-b292-0f3e13452c35"> <dynamicField name="1">-2.073420455529934786</dynamicField> </dynamicEntry> </attr> <attributes> <dynamicAttr name="10">8</dynamicAttr> <dynamicAttr name="13">7</dynamicAttr> <dynamicAttr name="27">3</dynamicAttr> <dynamicAttr name="4">2019-11-05T20:30:57.6Z</dynamicAttr> <dynamicAttr name="8">6</dynamicAttr> </attributes> <guid>80974561-a449-4a94-8b3e-970822b84406</guid> <lastModified>2019-11-09T01:44:34.157Z</lastModified> </element> </root>
第二步:自定义管道组件序列化JSON
在管道组件里用Newtonsoft.Json(BizTalk环境建议用稳定的12.x版本,避免兼容性问题)遍历XML节点,把name属性作为JSON的键值:
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg) { // 读取输入XML var xmlStream = pInMsg.BodyPart.GetOriginalDataStream(); var xmlDoc = new XmlDocument(); xmlDoc.Load(xmlStream); // 构建attr节点的JSON对象 var dynamicEntry = xmlDoc.SelectSingleNode("//attr/dynamicEntry"); var attrObj = new JObject(); var fieldNode = dynamicEntry.SelectSingleNode("dynamicField"); attrObj[dynamicEntry.Attributes["name"].Value] = new JObject { [fieldNode.Attributes["name"].Value] = fieldNode.InnerText }; // 构建attributes节点的JSON对象 var attributesObj = new JObject(); foreach (XmlNode attrNode in xmlDoc.SelectNodes("//attributes/dynamicAttr")) { attributesObj[attrNode.Attributes["name"].Value] = attrNode.InnerText; } // 组合最终JSON var finalJson = new JObject { ["attr"] = attrObj, ["guid"] = xmlDoc.SelectSingleNode("//guid").InnerText, ["anotherGuid"] = xmlDoc.SelectSingleNode("//anotherGuid").InnerText, ["lastModified"] = xmlDoc.SelectSingleNode("//lastModified").InnerText, ["attributes"] = attributesObj }; // 将JSON写入输出流 var jsonStream = new MemoryStream(Encoding.UTF8.GetBytes(finalJson.ToString())); pInMsg.BodyPart.Data = jsonStream; pInMsg.Context.Promote("MessageType", "http://schemas.microsoft.com/BizTalk/2003/system-properties", "application/json"); return pInMsg; }
方案2:前缀替换+XSLT转换
如果不想大幅调整现有XML结构,可以用XSLT给无效节点名加前缀,转成有效XML后再在管道组件里去掉前缀生成JSON。
第一步:用XSLT转换无效XML
编写XSLT脚本,给数字节点加attr_前缀,给GUID节点加guid_前缀:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <!-- 处理数字开头的节点 --> <xsl:template match="*[starts-with(local-name(), '1') or starts-with(local-name(), '2') or starts-with(local-name(), '3') or starts-with(local-name(), '4') or starts-with(local-name(), '8')]"> <xsl:element name="attr_{local-name()}"> <xsl:apply-templates/> </xsl:element> </xsl:template> <!-- 处理GUID格式的节点 --> <xsl:template match="*[contains(local-name(), '-')]"> <xsl:element name="guid_{local-name()}"> <xsl:apply-templates/> </xsl:element> </xsl:template> </xsl:stylesheet>
把这个XSLT加到BizTalk映射里,或者作为管道组件的前置步骤。
第二步:管道组件中去除前缀生成JSON
在管道组件序列化时,把节点名的attr_和guid_前缀去掉,作为JSON的键:
// 以attributes节点为例 foreach (XmlNode node in xmlDoc.SelectNodes("//attributes/*")) { var key = node.LocalName.Replace("attr_", ""); attributesObj[key] = node.InnerText; }
方案3:BizTalk映射配合自定义XSLT脚本
如果需要在BizTalk映射阶段就完成动态结构的转换,可以用自定义XSLT把动态节点转成键值对列表,再传给管道组件处理。
在BizTalk映射里添加自定义XSLT脚本,把attributes下的动态节点转成<keyValue>结构:
<xsl:for-each select="attributes/*"> <keyValue> <key><xsl:value-of select="local-name()"/></key> <value><xsl:value-of select="."/></value> </keyValue> </xsl:for-each>
然后管道组件读取这些<keyValue>节点,遍历生成动态JSON属性。
方案对比与选择
- 方案1:最推荐,结构规范,序列化逻辑简单,后期维护成本低,完全符合XML和JSON的最佳实践。
- 方案2:适合现有XML结构改动受限的场景,但需要维护XSLT的节点匹配规则,新增动态键时要更新XSLT。
- 方案3:适合需要在映射阶段处理业务逻辑的场景,但映射复杂度会提高。
不管选哪个方案,记得把自定义管道组件注册到GAC,并且在BizTalk主机实例中配置好权限,测试时一定要覆盖所有可能的动态键场景。
内容的提问来源于stack exchange,提问作者mburm
相关产品推荐
相关产品推荐

