Azure APIM XML转JSON单元素未返回JSON数组解决方案
问题描述
调用返回XML格式响应的WCF服务时,在Azure APIM中配置如下策略实现XML到JSON的格式转换:
<xml-to-json kind="direct" apply="always" consider-accept-header="false" />
当Titles节点下包含多个Title子元素时转换逻辑正常,但当单个Titles节点下仅存在1个Title元素时,转换结果不符合预期。
测试用原始XML结构如下:
<TitlesResponse> <Titles> <Title> <Name>BookTitle-1</Name> <Author>BookTitle-2</Author> </Title> <Title> <Name>BookTitle-1</Name> <Author>BookTitle-2</Author> </Title> </Titles> <Titles> <Title> <Name>BookTitle-1</Name> </Title> </Titles> </TitlesResponse>
默认策略转换输出的JSON如下,单Title场景下该字段返回对象类型,客户端要求Title字段始终返回数组类型:
{ "TitlesResponse": { "Titles": [ { "Title": [ { "Name": "BookTitle-1", "Author": "BookTitle-2" }, { "Name": "BookTitle-1", "Author": "BookTitle-2" } ] }, { "Title": { "Name": "BookTitle-1" } } ] } }
此前尝试在出站set-body策略中添加如下C#代码处理,但未生效:
string xmlContent = context.Response.Body.As<string>(preserveContent: true); XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlContent); return JsonConvert.SerializeObject(doc);
可行解决方案
共两种经过验证的实现方案,优先选择第一种。
方案1:XML预处理添加数组标记(推荐)
APIM内置的xml-to-json策略底层基于Json.NET实现,支持识别特定命名空间的属性强制指定节点序列化为数组。只需要在XML转JSON之前,通过XSLT转换给所有Title节点添加json:Array="true"属性即可,不需要改动原有转换逻辑,性能更优。
完整出站策略配置如下:
<outbound> <base /> <!-- XSLT预处理XML,给目标节点加强制数组标记 --> <xsl-transform> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:json="http://james.newtonking.com/projects/json"> <xsl:output method="xml" indent="yes"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 匹配所有Title节点,添加数组标记 --> <xsl:template match="Title"> <xsl:copy> <xsl:attribute name="json:Array">true</xsl:attribute> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> </xsl-transform> <!-- 原有XML转JSON逻辑保持不变 --> <xml-to-json kind="direct" apply="always" consider-accept-header="false" /> </outbound>
配置完成后,无论Title节点下有多少个子元素,转换后都会固定输出为JSON数组类型。
方案2:JSON转换后递归修正结构
如果不想使用XSLT,也可以在XML转JSON完成后,通过C#代码遍历JSON结构,将非数组类型的Title字段包装为数组。
注意:此前C#代码不生效的原因是直接序列化原始XmlDocument,生成的JSON结构和APIM内置转换的输出结构不一致,必须在xml-to-json策略执行完成后再运行修正代码。
出站set-body中使用的C#代码如下:
// 获取已经完成XML转JSON的响应内容 JObject responseContent = context.Response.Body.As<JObject>(preserveContent: true); void NormalizeToExpectedArray(JToken token) { if (token.Type == JTokenType.Object) { foreach (var property in token.Children<JProperty>().ToList()) { // 匹配Title字段,非数组则包装为数组 if (property.Name == "Title" && property.Value.Type != JTokenType.Array) { property.Value = new JArray(property.Value); } NormalizeToExpectedArray(property.Value); } } else if (token.Type == JTokenType.Array) { foreach (var item in token.Children()) { NormalizeToExpectedArray(item); } } } NormalizeToExpectedArray(responseContent); return responseContent.ToString();
内容的提问来源于stack exchange,提问作者Ranjith Marutharaj
相关产品推荐
相关产品推荐

