删除 WCF 输出中重复的 xmlns 声明

发布于 2024-11-19 18:07:02 字数 1387 浏览 1 评论 0 原文

我有 WCF 服务,它可以接受并返回 List> 形式的相当大量的数据。我的问题是典型行的输出如下所示:

   <d:ArrayOfanyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">PJ123</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">2</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">216565</d:anyType>
    <d:anyType i:type="e:dateTime" xmlns:e="http://www.w3.org/2001/XMLSchema">1993-09-10T00:00:00</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">Timesheet W/E 11/9/93 Franklin</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">CONSULT OF</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">25</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">0</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">25</d:anyType>
  </d:ArrayOfanyType>

因此,每行中的每个值都定义了“e”命名空间,这大大增加了消息的大小。我知道这背后的原因是 DataContractSerializer 不必进行 2 次传递,但以更大的消息大小为代价来获得一点处理性能似乎很有限。我考虑过如何更改数据结构,但字段实际上可以是任何东西,我无法避免使用 元素。

有谁知道拦截 WCF 管道并删除/重新组织这些名称空间声明的方法吗?

I have WCF service that can accept and return fairly large amounts of data in the form of a List<List<object>>. My problem is that the output of a typical row looks like this:

   <d:ArrayOfanyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">PJ123</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">2</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">216565</d:anyType>
    <d:anyType i:type="e:dateTime" xmlns:e="http://www.w3.org/2001/XMLSchema">1993-09-10T00:00:00</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">Timesheet W/E 11/9/93 Franklin</d:anyType>
    <d:anyType i:type="e:string" xmlns:e="http://www.w3.org/2001/XMLSchema">CONSULT OF</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">25</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">0</d:anyType>
    <d:anyType i:type="e:double" xmlns:e="http://www.w3.org/2001/XMLSchema">25</d:anyType>
  </d:ArrayOfanyType>

So every value in each row has the "e" namespace defined which greatly increases the size of the message. I know the reasoning behind this is so that the DataContractSerializer doesn't have to make 2 passes but it seems potty to gain a little processing performance at the expense of MUCH bigger message sizes. I've thought about how I could change the data structure but the fields really could be anything to I can't avoid the use of <anyType> elements.

Does anyone know of a way of intercepting the WCF pipeline and removing/reorganising these namespace declarations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

请叫√我孤独 2024-11-26 18:07:03

您可以使用自定义编码器来做到这一点 - 此时您可以随心所欲地使用 XML。正如约翰·桑德斯(John Saunders)指出的那样,这将导致(可能是重大的)性能下降,但它确实有效。下面的代码显示了您将如何处理它。

public class StackOverflow_6681219
{
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        List<List<object>> GetData();
    }
    public class Service : ITest
    {
        public List<List<object>> GetData()
        {
            return new List<List<object>>
            {
                new List<object>
                {
                    "PJ123",
                    2.0,
                    "216565",
                    new DateTime(1993, 9, 10),
                    "Timesheet W/E 11/9/93 Franklin",
                    "CONSULT OF",
                    25.0,
                    0.0,
                    25.0
                }
            };
        }
    }
    class MyEncodingBindingElement : MessageEncodingBindingElement
    {
        MessageEncodingBindingElement inner;
        public MyEncodingBindingElement(MessageEncodingBindingElement inner)
        {
            this.inner = inner;
        }

        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new MyEncoderFactory(this.inner.CreateMessageEncoderFactory());
        }

        public override MessageVersion MessageVersion
        {
            get { return this.inner.MessageVersion; }
            set { this.inner.MessageVersion = value; }
        }

        public override BindingElement Clone()
        {
            return new MyEncodingBindingElement(this.inner);
        }

        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            return context.CanBuildInnerChannelListener<TChannel>();
        }

        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
        }

        class MyEncoderFactory : MessageEncoderFactory
        {
            MessageEncoderFactory inner;
            public MyEncoderFactory(MessageEncoderFactory inner)
            {
                this.inner = inner;
            }

            public override MessageEncoder Encoder
            {
                get { return new MyEncoder(this.inner.Encoder); }
            }

            public override MessageVersion MessageVersion
            {
                get { return this.inner.MessageVersion; }
            }
        }

        class MyEncoder : MessageEncoder
        {
            MessageEncoder inner;
            public MyEncoder(MessageEncoder inner)
            {
                this.inner = inner;
            }

            public override string ContentType
            {
                get { return this.inner.ContentType; }
            }

            public override string MediaType
            {
                get { return this.inner.MediaType; }
            }

            public override bool IsContentTypeSupported(string contentType)
            {
                return this.inner.IsContentTypeSupported(contentType);
            }

            public override MessageVersion MessageVersion
            {
                get { return this.inner.MessageVersion; }
            }

            public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
            {
                return this.inner.ReadMessage(buffer, bufferManager, contentType);
            }

            public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
            {
                throw new NotImplementedException();
            }

            public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
            {
                const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
                const string SchemaNamespace = "http://www.w3.org/2001/XMLSchema";
                const string ArraysNamespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
                const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
                ArraySegment<byte> temp = this.inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
                MemoryStream ms = new MemoryStream(temp.Array, temp.Offset, temp.Count);
                XmlDocument doc = new XmlDocument();
                doc.Load(ms);
                bufferManager.ReturnBuffer(temp.Array);
                XmlAttribute rootAttr = doc.CreateAttribute("xmlns", "sch", XmlnsNamespace);
                rootAttr.Value = SchemaNamespace;
                doc.DocumentElement.Attributes.Append(rootAttr);
                XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
                nsManager.AddNamespace("arrays", ArraysNamespace);
                foreach (XmlNode arrayItem in doc.SelectNodes("//arrays:anyType", nsManager))
                {
                    XmlAttribute toRemove = null;
                    XmlAttribute typeAttr = null;
                    foreach (XmlAttribute attr in arrayItem.Attributes)
                    {
                        if (attr.Prefix == "xmlns" && attr.Value == SchemaNamespace)
                        {
                            toRemove = attr;
                        }
                        else if (attr.LocalName == "type" && attr.NamespaceURI == SchemaInstanceNamespace)
                        {
                            typeAttr = attr;
                        }
                    }

                    if (toRemove != null)
                    {
                        arrayItem.Attributes.Remove(toRemove);
                        if (typeAttr != null)
                        {
                            string prefix = toRemove.LocalName;
                            typeAttr.Value = typeAttr.Value.Replace(prefix + ":", "sch:");
                        }
                    }
                }
                ms = new MemoryStream();
                doc.Save(ms);
                byte[] buffer = bufferManager.TakeBuffer((int)ms.Length + messageOffset);
                Array.Copy(ms.GetBuffer(), 0, buffer, messageOffset, (int)ms.Length);
                return new ArraySegment<byte>(buffer, messageOffset, (int)ms.Length);
            }

            public override void WriteMessage(Message message, Stream stream)
            {
                throw new NotImplementedException();
            }
        }
    }
    static Binding ReplaceEncoding(Binding original)
    {
        CustomBinding custom = new CustomBinding(original);
        for (int i = 0; i < custom.Elements.Count; i++)
        {
            MessageEncodingBindingElement mebe = custom.Elements[i] as MessageEncodingBindingElement;
            if (mebe != null)
            {
                custom.Elements[i] = new MyEncodingBindingElement(mebe);
                break;
            }
        }

        return custom;
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        Binding binding = ReplaceEncoding(new BasicHttpBinding());
        host.AddServiceEndpoint(typeof(ITest), binding, "");
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        Console.WriteLine(proxy.GetData());

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

You can use a custom encoder to do that - at that point you can play with the XML as much as you want. As John Saunders pointed out, this will incur a (possibly significant) performance hit, but it certainly works. The code below shows how you'd do about it.

public class StackOverflow_6681219
{
    [ServiceContract]
    public interface ITest
    {
        [OperationContract]
        List<List<object>> GetData();
    }
    public class Service : ITest
    {
        public List<List<object>> GetData()
        {
            return new List<List<object>>
            {
                new List<object>
                {
                    "PJ123",
                    2.0,
                    "216565",
                    new DateTime(1993, 9, 10),
                    "Timesheet W/E 11/9/93 Franklin",
                    "CONSULT OF",
                    25.0,
                    0.0,
                    25.0
                }
            };
        }
    }
    class MyEncodingBindingElement : MessageEncodingBindingElement
    {
        MessageEncodingBindingElement inner;
        public MyEncodingBindingElement(MessageEncodingBindingElement inner)
        {
            this.inner = inner;
        }

        public override MessageEncoderFactory CreateMessageEncoderFactory()
        {
            return new MyEncoderFactory(this.inner.CreateMessageEncoderFactory());
        }

        public override MessageVersion MessageVersion
        {
            get { return this.inner.MessageVersion; }
            set { this.inner.MessageVersion = value; }
        }

        public override BindingElement Clone()
        {
            return new MyEncodingBindingElement(this.inner);
        }

        public override bool CanBuildChannelListener<TChannel>(BindingContext context)
        {
            return context.CanBuildInnerChannelListener<TChannel>();
        }

        public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
        {
            context.BindingParameters.Add(this);
            return context.BuildInnerChannelListener<TChannel>();
        }

        class MyEncoderFactory : MessageEncoderFactory
        {
            MessageEncoderFactory inner;
            public MyEncoderFactory(MessageEncoderFactory inner)
            {
                this.inner = inner;
            }

            public override MessageEncoder Encoder
            {
                get { return new MyEncoder(this.inner.Encoder); }
            }

            public override MessageVersion MessageVersion
            {
                get { return this.inner.MessageVersion; }
            }
        }

        class MyEncoder : MessageEncoder
        {
            MessageEncoder inner;
            public MyEncoder(MessageEncoder inner)
            {
                this.inner = inner;
            }

            public override string ContentType
            {
                get { return this.inner.ContentType; }
            }

            public override string MediaType
            {
                get { return this.inner.MediaType; }
            }

            public override bool IsContentTypeSupported(string contentType)
            {
                return this.inner.IsContentTypeSupported(contentType);
            }

            public override MessageVersion MessageVersion
            {
                get { return this.inner.MessageVersion; }
            }

            public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
            {
                return this.inner.ReadMessage(buffer, bufferManager, contentType);
            }

            public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
            {
                throw new NotImplementedException();
            }

            public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
            {
                const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
                const string SchemaNamespace = "http://www.w3.org/2001/XMLSchema";
                const string ArraysNamespace = "http://schemas.microsoft.com/2003/10/Serialization/Arrays";
                const string SchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";
                ArraySegment<byte> temp = this.inner.WriteMessage(message, maxMessageSize, bufferManager, messageOffset);
                MemoryStream ms = new MemoryStream(temp.Array, temp.Offset, temp.Count);
                XmlDocument doc = new XmlDocument();
                doc.Load(ms);
                bufferManager.ReturnBuffer(temp.Array);
                XmlAttribute rootAttr = doc.CreateAttribute("xmlns", "sch", XmlnsNamespace);
                rootAttr.Value = SchemaNamespace;
                doc.DocumentElement.Attributes.Append(rootAttr);
                XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
                nsManager.AddNamespace("arrays", ArraysNamespace);
                foreach (XmlNode arrayItem in doc.SelectNodes("//arrays:anyType", nsManager))
                {
                    XmlAttribute toRemove = null;
                    XmlAttribute typeAttr = null;
                    foreach (XmlAttribute attr in arrayItem.Attributes)
                    {
                        if (attr.Prefix == "xmlns" && attr.Value == SchemaNamespace)
                        {
                            toRemove = attr;
                        }
                        else if (attr.LocalName == "type" && attr.NamespaceURI == SchemaInstanceNamespace)
                        {
                            typeAttr = attr;
                        }
                    }

                    if (toRemove != null)
                    {
                        arrayItem.Attributes.Remove(toRemove);
                        if (typeAttr != null)
                        {
                            string prefix = toRemove.LocalName;
                            typeAttr.Value = typeAttr.Value.Replace(prefix + ":", "sch:");
                        }
                    }
                }
                ms = new MemoryStream();
                doc.Save(ms);
                byte[] buffer = bufferManager.TakeBuffer((int)ms.Length + messageOffset);
                Array.Copy(ms.GetBuffer(), 0, buffer, messageOffset, (int)ms.Length);
                return new ArraySegment<byte>(buffer, messageOffset, (int)ms.Length);
            }

            public override void WriteMessage(Message message, Stream stream)
            {
                throw new NotImplementedException();
            }
        }
    }
    static Binding ReplaceEncoding(Binding original)
    {
        CustomBinding custom = new CustomBinding(original);
        for (int i = 0; i < custom.Elements.Count; i++)
        {
            MessageEncodingBindingElement mebe = custom.Elements[i] as MessageEncodingBindingElement;
            if (mebe != null)
            {
                custom.Elements[i] = new MyEncodingBindingElement(mebe);
                break;
            }
        }

        return custom;
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        Binding binding = ReplaceEncoding(new BasicHttpBinding());
        host.AddServiceEndpoint(typeof(ITest), binding, "");
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
        ITest proxy = factory.CreateChannel();
        Console.WriteLine(proxy.GetData());

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文