XmlSerializer反序列化失败

发布于 2024-08-27 21:37:49 字数 2134 浏览 4 评论 0 原文

我有来自第三方服务器的 wsdl。运行 svcutil 并最终得到一组

XmlNode AMethod(object Request);

方法。有一个单独的 100 页 pdf 描述每个方法的响应/请求对象

我的想法是包装 Web 方法并使用 XmlSerializer 返回强类型对象。返回的 xml 看起来像这样(我删除了肥皂头):

<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:type="ResponseExt" 
        xmlns="http://www.thirdparty.com/lr/">
  <Code>0</Code>
  <Message>SUCCESS</Message>
  <SessionId>session_token</SessionId>
</Response>

看起来很简单。创建了一个类(从文档/线路捕获):

[XmlRoot("Response")]
//EDIT added XmlType
[XmlType("ResponseExt", Namespace = "http://www.thirdparty.com/lr/")]
public class MyClass {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

处理时间:

//XmlNode node = xml from above
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlNodeReader reader =  new XmlNodeReader(node);
Myclass myclass = serializer.Deserialize(reader) as MyClass

最后一行是它因内部异常消息而爆炸的地方:无法识别指定的类型:name='ResponseExt',namespace='http://www.thirdparty.com/lr/',位于 >.
我不知道如何让 Serializer 满意以及这两者到底意味着什么

xsi:type="ResponseExt" xmlns="http://www.thirdparty.com/lr/

一如既往,任何建议和指针都值得赞赏


编辑:接受下面的答案。

我仍然遇到异常,直到我发现这个,希望它会节省一些时间。 我开始逆向工作。在线捕获 xml。反序列化为我创建的具有正确属性的类:工作得非常顺利。从网络服务再次尝试 - 异常。由于某种原因,XmlSerializer 无法识别 ResponseExt。

XmlSerializer serializer = new XmlSerializer(typeof(Response));
XmlNode node = (XmlNode)results[0];
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.OuterXml); //reload node
XmlNodeReader reader = new XmlNodeReader(doc.FirstChild); //there is only one node
Response rsp = serializer.Deserialize(reader) as Response; //works

编辑:底层问题 wsdl 文件不完整。在花了 2 天时间并找到这个(丑陋的)解决方法后,第三方供应商提供了完整 WSDL,其中包含反序列化时没有错误的所有类型。

I have wsdl from third party server. Ran svcutil and ended up wih a set of

XmlNode AMethod(object Request);

methods. There is a separate 100 page pdf describing response/request objects for each method

My thought was wrap web methods and use XmlSerializer to return strongly typed objects. Returned xml looks like this (i removed soap headers):

<Response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xsi:type="ResponseExt" 
        xmlns="http://www.thirdparty.com/lr/">
  <Code>0</Code>
  <Message>SUCCESS</Message>
  <SessionId>session_token</SessionId>
</Response>

Looked simple. Created a class(from document/wire captures):

[XmlRoot("Response")]
//EDIT added XmlType
[XmlType("ResponseExt", Namespace = "http://www.thirdparty.com/lr/")]
public class MyClass {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

Processing time:

//XmlNode node = xml from above
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
XmlNodeReader reader =  new XmlNodeReader(node);
Myclass myclass = serializer.Deserialize(reader) as MyClass

Last line is where it blows up with inner exception message: The specified type was not recognized: name='ResponseExt', namespace='http://www.thirdparty.com/lr/', at <Response xmlns=''>.
I can't figure out how to make Serializer happy and what exactly these two mean

xsi:type="ResponseExt"
xmlns="http://www.thirdparty.com/lr/

As always any advice and pointer are appreciated


EDIT: Accepted answer below.

I was still getting exception, until i found this, hopefully it'll save someone some time.
I started to work backwards. Captured xml on the wire. Deserialized to my created classes with correct attributes: worked like a charm. Tried again from webservice - exception. For some reason XmlSerializer doesn't recognize ResponseExt.

XmlSerializer serializer = new XmlSerializer(typeof(Response));
XmlNode node = (XmlNode)results[0];
XmlDocument doc = new XmlDocument();
doc.LoadXml(node.OuterXml); //reload node
XmlNodeReader reader = new XmlNodeReader(doc.FirstChild); //there is only one node
Response rsp = serializer.Deserialize(reader) as Response; //works

EDIT: underlying issue wsdl file was not complete. After spending 2 days on this and finding this (ugly) workaround, third-party vendor provided complete WSDL with all types that deserialize without errors.

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

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

发布评论

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

评论(1

挽袖吟 2024-09-03 21:37:49

当您拥有 WSDL 时,为什么还要手动反序列化 XML?

如果您有 WSDL,请使用 svcutil.exe 工具或 wsdl.exe 工具为在线发送和接收的 XML 消息生成代理类和 DTO。

Web 服务工具包或“堆栈”的目的是为您提供此服务,这样您就不必手动编写类和 XML 序列化代码。

你尝试过这个吗?您是否尝试过通过这些工具之一运行 WSDL?或者您是否尝试在 Visual Studio 中“添加 Web 引用”?


更新问题后,我建议您修改WSDL,而不是编写自定义代码。您可以为服务生成自定义 WSDL,它将正确生成您想要的代理类。如果您不需要全部 100 个方法(或者无论有多少个方法),那么请将它们排除在外。如果您想要从方法中获得自定义对象,请定义与该对象相对应的复杂类型。这比为每个方法手动编写 XML 反序列化代码更简单、更可靠。


如果您不喜欢这个想法,并且想要坚持手动编写 XML 反序列化代码,那么您需要做两件事:

  1. attach XmlRoot 属性的命名空间

  2. 将类的名称更改为 ResponseExt,并从名为 Response 的类派生它。使用 XmlInclude 属性装饰该 Response 类。这使 Xml 序列化程序与 xsi:type 的使用保持一致在 XML 片段中使用

代码如下:

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

public class XsiType
{
    public static void Main(string[] args)
    {
        try
        {
            string filename = "XsiType.xml";
            XmlSerializer s1 = new XmlSerializer(typeof(Response));
            ResponseExt r = null;
            using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
            {
                r= (ResponseExt) s1.Deserialize(reader);
            }

            var builder = new System.Text.StringBuilder();
            var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
            using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
            {
                //s1.Serialize(writer, r, ns);
                s1.Serialize(writer, r);
            }
            string xml = builder.ToString();
            System.Console.WriteLine(xml);

        }
        catch (System.Exception exc1)
        {
            Console.WriteLine("Exception: {0}", exc1.ToString());
        }
    }
}

相关:如何强制使用 xsi:type 属性?

Why are you manually deserializing XML, when you have WSDL ?

If you have WSDL, use the svcutil.exe tool, or the wsdl.exe tool, to generate proxy classes and DTOs for the XML messages being sent and received on the wire.

The point of a web services toolkit, or "stack" is to provide this for you, so that you don't have to author classes and XML serialization code by hand.

Did you try this? Did you try to run the WSDL through one of those tools? Or did you try to "Add web reference" in Visual Studio?


After updating the question, I suggest that you modify the WSDL, rather than write custom code. You can produce a custom WSDL for the service, which will correctly generate the proxy classes you want. If you don't need all 100 methods (or however many there are), then leave them out. If you want a custom object from a method, then define a complexType that corresponds to that object. This is much simpler and more reliable than hand-authoring XML deserialization code for each method.


If you don't like that idea, and want to stick with manually writin the XML deserialization code, then you need to do two things:

  1. attach a namespace to the XmlRoot attribute.

  2. change the name of your class to ResponseExt, and derive it from a class called Response. Decorate that Response class with an XmlInclude attribute. This aligns the use of the Xml Serializer with the xsi:type used in the XML fragment.

It looks like this in code:

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
    public string Code {get; set;}
    public string Message {get; set;}
    public string SessionId {get; set;}
}

public class XsiType
{
    public static void Main(string[] args)
    {
        try
        {
            string filename = "XsiType.xml";
            XmlSerializer s1 = new XmlSerializer(typeof(Response));
            ResponseExt r = null;
            using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
            {
                r= (ResponseExt) s1.Deserialize(reader);
            }

            var builder = new System.Text.StringBuilder();
            var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
            using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
            {
                //s1.Serialize(writer, r, ns);
                s1.Serialize(writer, r);
            }
            string xml = builder.ToString();
            System.Console.WriteLine(xml);

        }
        catch (System.Exception exc1)
        {
            Console.WriteLine("Exception: {0}", exc1.ToString());
        }
    }
}

related: How can I force the use of an xsi:type attribute?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文