BizTalk:XSLT1.0中如何将多节点值传入C#函数获取目标用户?
问题描述
需要用XSLT 1.0处理指定XML数据,筛选出CookTime时间戳最新的Burger节点,并将对应的User值写入<LatestBurgerCook>节点。
原始XML数据
<xml> <Burger> <CookTime>"2023-04-03T15:13:33.8100000"</CookTime> <User>Billy</User> </Burger> <Burger> <CookTime>"2023-04-01T15:13:33.8100000"</CookTime> <User>Bob</User> </Burger> <Burger> <CookTime>"2023-04-03T17:13:33.8100000"</CookTime> <User>Jill</User> </Burger> </xml>
预期输出
<LatestBurgerCook> Jill </LatestBurgerCook>
尝试的实现方式
尝试通过调用C#函数传递多个CookTime节点实现逻辑,编写的XSLT片段如下:
<xsl:variable name="maxCookedTime"> <xsl:value-of select="userCSharp:LoadLatestCookTime(xml/Burger/CookTime)"/> </xsl:variable name="maxCookedTime">
对应的C#函数(无法正常工作):
public static string LoadLatestCookTime(XPathNodeIterator nodes) { List<DateTime> dateTimes = new List<DateTime>(); foreach(var node in nodes) dateTimes.Add(Convert.ToDateTime(s)); DateTime maximumDateTime = dateTimes.Max(); return maximumDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffffff"); }
咨询问题
- 上述方式是否可行?若方法有误,问题出在哪里?
- 在仅支持XSLT 1.0 + MSXSL C#函数的限制下,还有哪些可行方案?
解决方案
一、原方法的问题与修正
原思路可行,但C#函数存在两处关键错误:
- 遍历
XPathNodeIterator时未正确迭代:需调用MoveNext()才能遍历节点,直接foreach无法正常获取内容。 - 未正确处理节点值:代码中使用了未定义的变量
s,且未去除CookTime值前后的双引号,导致时间转换失败。
修正后的C#函数
public static string LoadLatestCookTime(XPathNodeIterator nodes) { List<DateTime> dateTimes = new List<DateTime>(); while (nodes.MoveNext()) { // 去除时间字符串前后的双引号后转换为DateTime string timeStr = nodes.Current.Value.Trim('"'); if (DateTime.TryParse(timeStr, out DateTime dt)) { dateTimes.Add(dt); } } if (dateTimes.Count == 0) return string.Empty; DateTime maximumDateTime = dateTimes.Max(); // 返回带双引号的格式,与原CookTime格式匹配 return $"\"{maximumDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffffff")}\""; }
配套完整XSLT代码
需先声明C#扩展函数的命名空间,再通过获取到的最大时间戳匹配对应Burger节点:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:userCSharp="urn:cs-functions" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="userCSharp msxsl"> <msxsl:script language="C#" implements-prefix="userCSharp"> <![CDATA[ public static string LoadLatestCookTime(XPathNodeIterator nodes) { List<DateTime> dateTimes = new List<DateTime>(); while (nodes.MoveNext()) { string timeStr = nodes.Current.Value.Trim('"'); if (DateTime.TryParse(timeStr, out DateTime dt)) { dateTimes.Add(dt); } } if (dateTimes.Count == 0) return string.Empty; DateTime maximumDateTime = dateTimes.Max(); return $"\"{maximumDateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffffff")}\""; } ]]> </msxsl:script> <xsl:template match="/"> <LatestBurgerCook> <xsl:variable name="maxTime" select="userCSharp:LoadLatestCookTime(xml/Burger/CookTime)"/> <xsl:value-of select="xml/Burger[CookTime = $maxTime]/User"/> </LatestBurgerCook> </xsl:template> </xsl:stylesheet>
二、纯XSLT 1.0方案(无需C#函数)
利用ISO8601时间戳的字符串排序特性,直接对Burger节点按CookTime降序排序,取第一个节点的User值:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <LatestBurgerCook> <xsl:for-each select="xml/Burger"> <!-- 去掉引号后按文本降序排序,ISO8601格式字符串大的时间更晚 --> <xsl:sort select="translate(CookTime, '"', '')" data-type="text" order="descending"/> <xsl:if test="position() = 1"> <xsl:value-of select="User"/> </xsl:if> </xsl:for-each> </LatestBurgerCook> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者Kieran Ojakangas
相关产品推荐
相关产品推荐

