如何将自定义命名空间添加到传入的XML消息?

时间:2012-05-17 06:51:35

标签: c# xml linq biztalk

向传入的XML消息添加名称空间的推荐方法是什么?为了分发传入的XML消息,我使用了一个信封以及一个“普通”消息xsd模式,它们都有一个目标命名空间。传入的XML消息首先没有附加任何名称空间。

谢谢

using System;
using System.Linq;
using System.Xml.Linq;

public class AddNamespaceRcvPipelineComponent
{
    static public void Main ()
    {
        XElement xDoc = XElement.Load(@"test.xml");

        XNamespace ns1 = "Namespace1";
        XNamespace ns2 = "Namespace2";

        foreach (XElement el in xDoc.DescendantsAndSelf("ORDERS"))
        {
            // el.Add(new XAttribute(XNamespace.Xmlns + "ns1", "Namespace1"));
            el.Name = ns1 + el.Name.LocalName;
        }

        foreach (XElement el in xDoc.DescendantsAndSelf("ORDER"))
        {
            // el.Add(new XAttribute(XNamespace.Xmlns + "ns2", "Namespace2"));
            el.Name = ns2 + el.Name.LocalName;
        }

        // TODO: Strip empty namespaces ...

        Console.WriteLine(xDoc);
    }
}

2 个答案:

答案 0 :(得分:2)

解决问题的规范方法确实是在传入管道的Decode阶段使用自定义管道组件。

以下两种方法可以有效地发挥作用。

使用BizTalk ESB Tookit管道组件

BizTalk ESB Toolkit 2.1具有一组非常强大的管道组件,用于处理传入消息中的XML命名空间。安装时,此管道在Microsoft.Practices.ESB.Namespace.PipelineComponents.dll程序集中可用made available到Visual Studio工具箱。

这些组件的早期版本为documented on MSDN。虽然这个文档已经过时了,但我认为新版本没有太大变化。尽管如此,我建议您参考各种forum questions以获取正确的用法和参考。

构建简单的AddXmlNamespace管道组件

如果您不想或不能在您的环境中安装BizTalk ESB Toolkit,我建议您自己构建一个简化版本的组件。这一点并不难,这要归功于您可以利用的BizTalk运行时中的内置类。

我正在显示下面要求的代码,因为您在问题中显示的代码不符合streaming-enabled pipeline components。请注意,此后显示的代码仅处理在原始根标记上添加XML命名空间(如果尚未存在)。

首先,您需要构建一个简单的System.IO.Stream派生类,该类处理在原始文档的根标记上添加XML名称空间和前缀。

为了支持流式传输,代码利用了Microsoft.BizTalk.Streaming.XmlTranslatorStream类。此类展示了类似SAX的接口,从而在XML解析阶段的各个点调用实现的覆盖。所有这些都是在保持对流式传输任意大型文档的完全支持的同时执行的。

这是代码:

using Microsoft.BizTalk.Streaming;

public class AddXmlNamespaceStream : XmlTranslatorStream
{
    private String namespace_;
    private int level_ = 0; // hierarchy level

    public AddXmlNamespaceStream(Stream stream, String @namespace)
        : base(XmlReader.Create(stream))
    {
        namespace_ = @namespace;
    }

    #region XmlTranslatorStream Overrides

    protected override void TranslateStartElement(string prefix, string localName, string nsURI)
    {
        if (level_++ != 0)
        {
            base.TranslateStartElement(prefix, localName, nsURI);
            return;
        }

        if (String.IsNullOrEmpty(nsURI))
        {
            nsURI = namespace_;
            if (String.IsNullOrEmpty(prefix))
                prefix = "__bts_ns0__";
        }

        base.TranslateStartElement(prefix, localName, nsURI);
    }

    protected override void TranslateEndElement(bool full)
    {
        if (level_-- != 0)
        {
            base.TranslateEndElement(full);
            return;
        }

        base.TranslateEndElement(full);
    }

    #endregion
}

您会注意到此类会覆盖TranslateStartElementTranslateEndElement方法,但只处理层次结构的第一级元素 - 这是根标记。所有其他元素都是根据基类提供的默认的do-nothing特殊行为进行处理的。

至于管道组件本身,我只会在这里显示它的Execute方法,因为您可能已经设置了所有必需的样板基础结构代码。如果情况并非如此,请从底部开始参阅博文​​at the following location

这是:

public class AddXmlNamespace : ..., IComponent
{
    #region Design-Time Properties

    public String Namespace { get; set; }

    #endregion

    #region IComponent Implementation

    public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
    {
        var stream = new AddXmlNamespaceStream( 
            pInMsg.BodyPart.GetOriginalDataStream()
            , Namespace);

        pInMsg.BodyPart.Data = stream;
        pContext.ResourceTracker.AddResource(stream);

        return pInMsg;
    }

    #endregion

    ...
}

如您所见,Execute方法所采取的唯一操作是将原始流包装在新AddXmlNamespace流类的实例中,并将其连接起来以替换传入的消息流。

希望这有帮助。

答案 1 :(得分:0)

ESB工具包有一个管道组件来执行此操作。