Serializable Class序列化为Xml,但相同的Xml不会反序列化

时间:2012-11-21 14:15:22

标签: c# xmlserializer invalidoperationexception

我们从WCF Web服务返回类AuthenticateUserOutput的实例。

所以我们有这个方法:

public override AuthenticateUserOutput AuthenticateUser(AuthenticateUserInput AuthenticateUserInput)

AuthenicateUserOutput是自动生成的:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.5420")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="xxxx")]
public partial class AuthenticateUserOutput : WS2MethodOutput
{

    private bool authenticationResultField;

    private UserContext userContextField;

    /// <remarks/>
    public bool AuthenticationResult
    {
        get
        {
            return this.authenticationResultField;
        }
        set
        {
            this.authenticationResultField = value;
        }
    }

    /// <remarks/>
    public UserContext UserContext
    {
        get
        {
            return this.userContextField;
        }
        set
        {
            this.userContextField = value;
        }
    }
}

我们需要能够将其序列化为AuthenticateUserOutput,但它不起作用。

作为测试,我实例化了AuthenticateUserOutput,对其进行了序列化并尝试对其进行反序列化。

它因InvalidOperationException而失败。

这是序列化的Xml:

<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
  <Response xmlns="xxxx">
    <IsValid>true</IsValid>
    <Success>true</Success>
  </Response>
  <AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
  <UserContext xmlns="xxxx">
  </UserContext>
</AuthenticateUserOutput>

这是序列化和反序列化代码:

    public static string ToXml(this object input)
    {
        string output;
        XmlSerializer serializer = new XmlSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();
        using (TextWriter textWriter = new StringWriter(sb))
        {
            serializer.Serialize(textWriter, input);
        }
        output = sb.ToString();
        return output;
    }

    public static T FromXml<T>(this string input)
    {
        T output;

        XmlSerializer serializer = new XmlSerializer(typeof(T));

        output = (T)serializer.Deserialize(new StringReader(input));

        return output;
    }

例外的具体细节:

System.InvalidOperationException occurred
  Message="<AuthenticateUserOutput xmlns='xxxx'> was not expected."
  Source="qkxd8dd-"
  StackTrace:
       at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderAuthenticateUserOutput.Read31_AuthenticateUserOutput()
  InnerException: 

因此它不能反序列化它自己的序列化Xml。

有人能看出原因吗?

谢谢,

J1M。

更新:以下是测试代码:

        AuthenticateUserOutput test = new AuthenticateUserOutput();

        test.AuthenticationResult = true;
        test.Response = new ResponseType();
        test.Response.Exception = null;
        test.Response.IsValid = true;
        test.Response.Success = true;
        test.Response.ValidationErrors = null;
        test.UserContext = new UserContext();

        string serializedXml = test.ToXml();

        AuthenticateUserOutput deserializedString = serializedXml.FromXml<AuthenticateUserOutput>();

UPDATE2 :Deserializer的工作得益于Mark Gravell

OK, I forgot I'd added this to get the namespaces to come out correctly when de-serializing:

public partial class WS2MethodInput
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces _xmlns;

    /// <summary>
    /// Constructor for WS2MethodInput that sets up default namespaces
    /// </summary>
    public WS2MethodInput()
    {
        _xmlns = new XmlSerializerNamespaces();
        _xmlns.Add("", "xxxx");
    }
}

有了这个,序列化的Xml就像在这条消息的顶部一样。 如果没有它,反序列化器可以工作,但AuthenticateUserOutput缺少名称空间:

<?xml version="1.0" encoding="utf-16"?>
<AuthenticateUserOutput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="xxxx">
  <Response xmlns="xxxx">
    <IsValid>true</IsValid>
    <Success>true</Success>
  </Response>
  <AuthenticationResult xmlns="xxxx">true</AuthenticationResult>
  <UserContext xmlns="xxxx">
  </UserContext>
</AuthenticateUserOutput>

请注意xmlns="xxxx"

末尾的AuthenticateUserOutput

问题是,现在我不能将Xml与我们的其他代码一起使用,而不是:

1)将其加载到XDocument并添加命名空间并在需要对其进行反序列化时将其删除 2)对字符串替换正则表达式或其他东西

进行相同的操作

我都不喜欢。事实上这太可怕了! 8X

1 个答案:

答案 0 :(得分:0)

问题是设置XmlSerializerNamespaces以强制根元素上的xmlns。

拿走它,它有效。

但是,现在我无法在其他任何地方使用序列化的Xml,因为它缺少命名空间。

我将开始一个专注于此的新问题。

谢谢,

J1M。

<强>更新

好吧,我想通了,所以我不打算写一个新问题。

鉴于以下XSD:

<xs:schema xmlns="urn:www-test-com:testservice" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:www-test-com:testservice" elementFormDefault="qualified" attributeFormDefault="unqualified">
    <xs:element name="AuthenticateUserInput">
        <xs:annotation>
            <xs:documentation>Used to provide user credentials to the AuthenticateUser method</xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Username" type="xs:string"/>
                <xs:element name="Password" type="xs:string"/>
                <xs:element name="Method" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

svcutil生成这样的东西,虽然我想有很多工具可以为XSD吐出类似的东西:

[Serializable()]
[XmlType(AnonymousType = true, Namespace = "urn:www-test-com:testservice")]
public class AuthenticateUserInput
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string Method { get; set; }
}

现在,使用“默认”XmlSerializer代码:

    static string ToXml(object input)
    {
        string output;

        XmlSerializer serializer = new XmlSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();

        using (XmlWriter xw = XmlWriter.Create(sb))
        {
            serializer.Serialize(xw, input);
        }
        output = sb.ToString();
        return output;
    }

您获得以下Xml:

<AuthenticateUserInput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Username xmlns="urn:www-test-com:testservice">sa</Username>
    <Password xmlns="urn:www-test-com:testservice">Password1</Password>
    <Method xmlns="urn:www-test-com:testservice">Plain</Method>
</AuthenticateUserInput>

哪个不符合架构(验证失败,这就是目前正在杀死我们的东西)

好的,所以,我找到了将[XmlType]更改为[XmlRoot]并且粗略测试有效的参考,如果我更改生成的类,我现在得到以下符合的Xml:

<AuthenticateUserInput xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:www-test-com:testservice">
    <Username>sa</Username>
    <Password>Password1</Password>
    <Method>Plain</Method>
</AuthenticateUserInput>

但我不想手动更改所有自动生成的类。所以一段时间后我找到了以下解决方案:

    static string ToXml(object input)
    {
        string output;

        XmlSerializer serializer = CreateSerializer(input.GetType());
        StringBuilder sb = new StringBuilder();

        var settings = new XmlWriterSettings() { OmitXmlDeclaration = true, Encoding =  Encoding.UTF8, Indent = true };

        using (XmlWriter xw = XmlWriter.Create(sb, settings))
        {
            serializer.Serialize(xw, input);
        }
        output = sb.ToString();
        return output;
    }

    static T FromXml<T>(string input)
    {
        T output;

        XmlSerializer serializer = CreateSerializer(typeof(T));

        output = (T)serializer.Deserialize(new StringReader(input));

        return output;
    }

    private static XmlSerializer CreateSerializer(Type incomingType)
    {
        XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
        XmlAttributes newAttributes = new XmlAttributes();

        newAttributes.XmlRoot = new XmlRootAttribute();
        newAttributes.XmlRoot.Namespace = ((XmlTypeAttribute)incomingType.GetCustomAttributes(typeof(XmlTypeAttribute), true)[0]).Namespace;
        attrOverrides.Add(incomingType, newAttributes);

        XmlSerializer serializer = new XmlSerializer(incomingType, attrOverrides);
        return serializer;
    }

正如您所看到的,您可以创建XmlAttributeOverrides而不是仅使用XmlSerializer的默认构造函数,并在其上添加一个XmlAttributes,您可以在其上实例化XmlRoot属性。 为了使这个更好和通用,它使用((XmlTypeAttribute)incomingType.GetCustomAttributes(typeof(XmlTypeAttribute), true)[0]).Namespace将传入类型的名称空间移出,如果类型没有该属性,则会失败,因此这个解决方案需要更多的工作,但你会得到一般的想法。 / p>

此致

J1M