无法将 XML 反序列化为 C# 对象,我做错了什么?

Failed to Deserialize XML to C# object, what am I doing wrong?

嗨,我需要 反序列化 XML 到 Parameters class 的对象中。

我定义了这个执行序列化的通用函数:

public static T Deserialize<T>(string xml) where T : class
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    using (StringReader reader = new StringReader(xml))
    {
        return (T)serializer.Deserialize(reader);
    }
}

XML 我要反序列化的字符串:

<Parameters>
    <UserProfileState>0</UserProfileState>
    <Parameter>
        <Name>Country</Name>
        <Type>String</Type>
        <Nullable>False</Nullable>
        <AllowBlank>False</AllowBlank>
        <MultiValue>True</MultiValue>
        <UsedInQuery>True</UsedInQuery>
        <State>MissingValidValue</State>
        <Prompt>Country</Prompt>
        <DynamicPrompt>False</DynamicPrompt>
        <PromptUser>True</PromptUser>
        <DynamicValidValues>True</DynamicValidValues>
        <DynamicDefaultValue>True</DynamicDefaultValue>
    </Parameter>
    <Parameter>
        <Name>City</Name>
        <Type>String</Type>
        <Nullable>False</Nullable>
        <AllowBlank>False</AllowBlank>
        <MultiValue>True</MultiValue>
        <UsedInQuery>True</UsedInQuery>
        <State>MissingValidValue</State>
        <Prompt>City</Prompt>
        <DynamicPrompt>False</DynamicPrompt>
        <PromptUser>True</PromptUser>
        <Dependencies>
            <Dependency>Country</Dependency>
        </Dependencies>
        <DynamicValidValues>True</DynamicValidValues>
        <DynamicDefaultValue>True</DynamicDefaultValue>
    </Parameter>
</Parameters>

键入 (class) 我想将我的 XML 反序列化为:

[Serializable, XmlRoot(ElementName = "Parameters")]
public class ParametersModel
{
    [XmlElement(ElementName = "UserProfileState")]
    public int UserProfileState { get; set; }
    [XmlArray(ElementName = "Parameter")]
    public List<ParameterModel> Parameter { get; set; }
}

[Serializable]
public class ParameterModel
{
    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }
    [XmlElement(ElementName = "Type")]
    public string Type { get; set; }
    [XmlElement(ElementName = "Nullable")]
    public bool Nullable { get; set; }
    [XmlElement(ElementName = "AllowBlank")]
    public bool AllowBlank { get; set; }
    [XmlElement(ElementName = "MultiValue")]
    public bool MultiValue { get; set; }
    [XmlElement(ElementName = "UsedInQuery")]
    public bool UsedInQuery { get; set; }
    [XmlElement(ElementName = "State")]
    public string State { get; set; }
    [XmlElement(ElementName = "Prompt")]
    public string Prompt { get; set; }
    [XmlElement(ElementName = "DynamicPrompt")]
    public bool DynamicPrompt { get; set; }
    [XmlElement(ElementName = "PromptUser")]
    public bool PromptUser { get; set; }
    [XmlElement(ElementName = "DynamicValidValues")]
    public bool DynamicValidValues { get; set; }
    [XmlElement(ElementName = "DynamicDefaultValue")]
    public bool DynamicDefaultValue { get; set; }

}

我从代码调用 反序列化 函数,传递这个 XML:

var parameters = Utility.Deserialize<ParametersModel>(xml);

现在我没有得到异常,但是参数集合是空的

怎么了?

谢谢

更新1

<?xml version="1.0" encoding="utf-16"?>
<Parameters xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <UserProfileState>0</UserProfileState>
  <Parameters>
    <Parameter />
    <Parameter />
  </Parameters>
</Parameters>

UserProfileState 下多了一个节点。

你们知道为什么我得到这个额外的节点,某些属性定义不正确吗?

更新2

我已经按照建议修改了目标对象 class,现在反序列化工作正常,唯一的问题是我必须将某些属性的类型从 bool 更改为 string ...现在我需要弄清楚如何保留 bool 属性并仍然进行转换....

[Serializable]
[XmlRoot(ElementName = "Parameters")]
public class ParametersModel
{
    [XmlElement(ElementName = "UserProfileState")]
    public int UserProfileState { get; set; }
    [XmlElement(ElementName = "Parameter")]
    public List<ParameterModel> Parameters { get; set; }
}

[Serializable]
public class ParameterModel
{
    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }
    [XmlElement(ElementName = "Type")]
    public string Type { get; set; }
    [XmlElement(ElementName = "Nullable")]
    public string Nullable { get; set; }
    [XmlElement(ElementName = "AllowBlank")]
    public string AllowBlank { get; set; }
    [XmlElement(ElementName = "MultiValue")]
    public string MultiValue { get; set; }
    [XmlElement(ElementName = "UsedInQuery")]
    public string UsedInQuery { get; set; }
    [XmlElement(ElementName = "State")]
    public string State { get; set; }
    [XmlElement(ElementName = "Prompt")]
    public string Prompt { get; set; }
    [XmlElement(ElementName = "DynamicPrompt")]
    public string DynamicPrompt { get; set; }
    [XmlElement(ElementName = "PromptUser")]
    public string PromptUser { get; set; }
    [XmlElement(ElementName = "DynamicValidValues")]
    public string DynamicValidValues { get; set; }
    [XmlElement(ElementName = "DynamicDefaultValue")]
    public string DynamicDefaultValue { get; set; }
}

更新3

对于布尔属性,我实施了建议的解决方法,"Nullable" 属性 是 bool 类型,所以我必须定义额外的 属性 将字符串自动转换为 bool 并自动将 bool 转换为字符串.谢谢大家的建议。

[Serializable]
public class ParameterModel
{
    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }
    [XmlElement(ElementName = "Type")]
    public string Type { get; set; }

    [XmlIgnore]
    public bool Nullable { get; set; }

    [XmlElement("Nullable")]
    public string NullableSerialize
    {
        get { return this.Nullable.ToString(); }
        set { this.Nullable = Convert.ToBoolean(value); }
    }

    [XmlElement(ElementName = "AllowBlank")]
    public string AllowBlank { get; set; }
    [XmlElement(ElementName = "MultiValue")]
    public string MultiValue { get; set; }
    [XmlElement(ElementName = "UsedInQuery")]
    public string UsedInQuery { get; set; }
    [XmlElement(ElementName = "State")]
    public string State { get; set; }
    [XmlElement(ElementName = "Prompt")]
    public string Prompt { get; set; }
    [XmlElement(ElementName = "DynamicPrompt")]
    public string DynamicPrompt { get; set; }
    [XmlElement(ElementName = "PromptUser")]
    public string PromptUser { get; set; }
    [XmlElement(ElementName = "DynamicValidValues")]
    public string DynamicValidValues { get; set; }
    [XmlElement(ElementName = "DynamicDefaultValue")]
    public string DynamicDefaultValue { get; set; }
}

您可能缺少 XML header 标签,例如

<?xml version="1.0" encoding="utf-8"?>

您的问题是您已告诉它反序列化为 ParametersModel 类型的对象。您没有给出元素名称的提示,因此它使用 class 名称作为元素名称。如果您要更改 xml 以使根元素为 ParametersModel,那么您会发现它不会再出错。但是,它也不会 return 您预期的结果,因为其他地方存在更多类似问题。

在评论中,Dan 建议从序列化开始最简单,他是对的。如果您尝试创建 ParametersModel 对象的实例,然后对其进行序列化,那么您将看到模型建议的名称和结构。如果您随后调整您的模型及其属性,直到输出符合您的预期,那么您就可以正确地反序列化 xml 文档。

从以下位置更改 ParametersModel 上的 XmlRootAttribute 声明:

[XmlRoot]

至:

[XmlRoot(ElementName = "Parameters")]

完整发布:

class Program
{
    static void Main(string[] args)
    {
        var s = @"<Parameters>
<UserProfileState>1</UserProfileState>
<Parameter>
    <Name>Country</Name>
    <Type>String</Type>
    <Nullable>false</Nullable>
    <AllowBlank>false</AllowBlank>
    <MultiValue>true</MultiValue>
    <UsedInQuery>true</UsedInQuery>
    <State>MissingValidValue</State>
    <Prompt>Country</Prompt>
    <DynamicPrompt>false</DynamicPrompt>
    <PromptUser>true</PromptUser>
    <DynamicValidValues>true</DynamicValidValues>
    <DynamicDefaultValue>true</DynamicDefaultValue>
</Parameter>
<Parameter>
    <Name>City</Name>
    <Type>String</Type>
    <Nullable>false</Nullable>
    <AllowBlank>false</AllowBlank>
    <MultiValue>true</MultiValue>
    <UsedInQuery>true</UsedInQuery>
    <State>MissingValidValue</State>
    <Prompt>City</Prompt>
    <DynamicPrompt>false</DynamicPrompt>
    <PromptUser>true</PromptUser>
    <Dependencies>
        <Dependency>Country</Dependency>
    </Dependencies>
    <DynamicValidValues>true</DynamicValidValues>
    <DynamicDefaultValue>true</DynamicDefaultValue>
</Parameter>
</Parameters>";

        var serializer = new XmlSerializer(typeof(ParametersModel));
        using (var reader = new StringReader(s))
        {
            var result = (ParametersModel)serializer.Deserialize(reader);
            var sb = new StringBuilder();
            using (var writer = new StringWriter(sb))
            {
                serializer.Serialize(writer, result);
            }
        }

    }
}

[Serializable]
[XmlRoot(ElementName ="Parameters")]
public class ParametersModel
{
    [XmlElement]
    public int UserProfileState { get; set; }

    [XmlElement("Parameter")]
    public List<ParameterModel> Parameters { get; set; }
}

[Serializable]
public class ParameterModel
{
    [XmlElement]
    public string Name { get; set; }
    [XmlElement]
    public string Type { get; set; }
    [XmlElement]
    public bool Nullable { get; set; }
    [XmlElement]
    public bool AllowBlank { get; set; }
    [XmlElement]
    public bool MultiValue { get; set; }
    [XmlElement]
    public bool UsedInQuery { get; set; }
    [XmlElement]
    public string State { get; set; }
    [XmlElement]
    public string Prompt { get; set; }
    [XmlElement]
    public bool DynamicPrompt { get; set; }
    [XmlElement]
    public bool PromptUser { get; set; }
    [XmlElement]
    public bool DynamicValidValues { get; set; }
    [XmlElement]
    public bool DynamicDefaultValue { get; set; }

}

如果您这样定义 类,则可以序列化 xml:

[Serializable]
[XmlRoot("Parameters")]
public class ParametersModel
{
    [XmlElement]
    public int UserProfileState { get; set; }   

    [XmlElement("Parameter")]   
    public List<ParameterModel> Parameters { get; set; }
}

但它不起作用,因为 XmlSerialize 无法将您的 False/True 解析为 bool 值。因此,您应该将所有 bool 属性更改为字符串,或者考虑使用 Microsoft 建议的解决方法: https://blogs.msdn.microsoft.com/helloworld/2009/04/03/workaround-to-deserialize-true-false-using-xmlserializer/

我认为下面的内容应该会让你很接近。首先,你的 top-level class

[XmlRoot(nameof(Parameters))]
public sealed class ParametersModel
{
    public int UserProfileState { get; set; }

    [XmlElement("Parameter")]
    public List<ParameterModel> Parameters { get; set; }
}

你的ParameterModelclass就是

public sealed class ParameterModel
{
    public string Name { get; set; }
    public string Type { get; set; }
    public BooleanAsString Nullable { get; set; }
    public BooleanAsString AllowBlank { get; set; }
    public BooleanAsString MultiValue { get; set; }
    public BooleanAsString UsedInQuery { get; set; }
    public string State { get; set; }
    public string Prompt { get; set; }
    public BooleanAsString DynamicPrompt { get; set; }
    public BooleanAsString PromptUser { get; set; }
    public List<Dependency> Dependencies { get; set; }
    public BooleanAsString DynamicValidValues { get; set; }
    public BooleanAsString DynamicDefaultValue { get; set; }
}

public sealed class Dependency
{
    [XmlText]
    public string Value { get; set; }
}

我已经在实用程序中完成了 bool/string 的工作 class

// https://blogs.msdn.microsoft.com/helloworld/2009/04/03/workaround-to-deserialize-true-false-using-xmlserializer/
public struct BooleanAsString
{
    public BooleanAsString(bool value = default(bool))
    {
        StringValue = null;
        Value = value;
    }
    public static implicit operator BooleanAsString(bool value)
    {
        return new BooleanAsString(value);
    }
    public static implicit operator bool(BooleanAsString value)
    {
        return value.Value;
    }

    [XmlIgnore]
    public bool Value
    {
        get { return Boolean.Parse(StringValue); }
        set { StringValue = value ? "True" : "False"; }
    }

    [XmlText]
    public string StringValue { get; set; }
}

测试代码为:

    static string XmlSerialize(Object o)
    {
        var serializer = new XmlSerializer(o.GetType());
        using (var writer = new StringWriter())
        {
            serializer.Serialize(writer, o);
            return writer.ToString();
        }
    }

    static void Main(string[] args)
    {
        var parameters = new ParametersModel { UserProfileState = 0, Parameters = new List<ParameterModel>() };
        parameters.Parameters.Add(new ParameterModel
        { Name = "County", Type = "String", Nullable = false, AllowBlank = false, MultiValue = true, UsedInQuery = true, State = "MissingValidValue", Prompt = "County", DynamicPrompt = false, PromptUser = true, DynamicValidValues = true, DynamicDefaultValue = true});
        var pm = new ParameterModel
        { Name = "City", Type = "String", Nullable = false, AllowBlank = false, MultiValue = true, UsedInQuery = true, State = "MissingValidValue", Prompt = "City", DynamicPrompt = false, PromptUser = true, DynamicValidValues = true, DynamicDefaultValue = true };
        pm.Dependencies = new List<Dependency>() { new Dependency{ Value = "Country" } };
        parameters.Parameters.Add(pm);

        var s = XmlSerialize(parameters);
    }

生成以下 XML

<?xml version="1.0" encoding="utf-16"?>
<Parameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <UserProfileState>0</UserProfileState>
  <Parameter>
    <Name>County</Name>
    <Type>String</Type>
    <Nullable>False</Nullable>
    <AllowBlank>False</AllowBlank>
    <MultiValue>True</MultiValue>
    <UsedInQuery>True</UsedInQuery>
    <State>MissingValidValue</State>
    <Prompt>County</Prompt>
    <DynamicPrompt>False</DynamicPrompt>
    <PromptUser>True</PromptUser>
    <DynamicValidValues>True</DynamicValidValues>
    <DynamicDefaultValue>True</DynamicDefaultValue>
  </Parameter>
  <Parameter>
    <Name>City</Name>
    <Type>String</Type>
    <Nullable>False</Nullable>
    <AllowBlank>False</AllowBlank>
    <MultiValue>True</MultiValue>
    <UsedInQuery>True</UsedInQuery>
    <State>MissingValidValue</State>
    <Prompt>City</Prompt>
    <DynamicPrompt>False</DynamicPrompt>
    <PromptUser>True</PromptUser>
    <Dependencies>
      <Dependency>Country</Dependency>
    </Dependencies>
    <DynamicValidValues>True</DynamicValidValues>
    <DynamicDefaultValue>True</DynamicDefaultValue>
  </Parameter>
</Parameters>