简体   繁体   English

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

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

Hi I need to deserialize XML into object of Parameters class. 嗨,我需要 XML 反序列化为Parameters类的对象。

I defined this generic function that does the serialization: 我定义了执行序列化的通用函数:

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 string I want to deserialize: 我想反序列化的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>

Type (class) I want to deserialize my XML into: 类型(类)我想反序列化我的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; }

}

I call my Deserialize function from code, passing this XML: 我通过代码调用我的Deserialize函数,并传递了以下XML:

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

Now I dont get an exception, but Parameter collection is Empty 现在我没有异常,但是参数集合为空

在此处输入图片说明

What is wrong? 怎么了?

Thank you 谢谢

UPDATE1 更新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>

There is an extra node under UserProfileState. UserProfileState下有一个额外的节点。

Do you guys know why I get this extra node, some attribute is defined incorrectly? 你们知道为什么我得到这个额外的节点,某些属性定义不正确吗?

UPDATE2 更新2

I have modified the target object class as was suggested into this, and the deserialization now works fine, with the only problem is that I had to change type from bool to string for some properties... now I need to figure out how to keep bool properties and still have conversion working.... 我已经按照建议的那样修改了目标对象类,并且反序列化现在可以正常工作,唯一的问题是我必须将某些属性的类型从bool更改为string ...现在我需要弄清楚如何保持布尔属性,并且仍具有转换功能。

[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; }
}

UPDATE3 更新3

For boolean properties I implemented suggested workaround, "Nullable" property is of type bool, so I had to define additional property that will translate string to bool and bool to string automatically. 对于布尔属性,我实现了建议的解决方法,“可为空”属性的类型为bool,因此我必须定义其他属性,该属性将字符串自动转换为bool并将bool自动转换为字符串。 Thank you guys for this suggestion. 谢谢你们的建议。

[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; }
}

You could serialize your xml if you define your classes this way: 如果以这种方式定义类,则可以序列化xml:

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

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

but it won't work, becasue XmlSerialize could not parse your False/True to bool values. 但是它不起作用,因为XmlSerialize无法将您的False / True解析为bool值。 So you should either change all your bool properties to string or consider to use this from microsoft suggested workaround: https://blogs.msdn.microsoft.com/helloworld/2009/04/03/workaround-to-deserialize-true-false-using-xmlserializer/ 因此,您应该将所有的bool属性更改为字符串,或者考虑使用Microsoft建议的解决方法: https : //blogs.msdn.microsoft.com/helloworld/2009/04/03/workaround-to-deserialize-true-false -using-xmlserializer /

I think the following should get you pretty close. 我认为以下内容将使您更加接近。 First, your top-level class 首先,您的顶级班

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

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

Your ParameterModel class is then 然后,您的ParameterModel

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; }
}

I've done the bool / string stuff in a utility class 我已经在实用程序类中完成了bool / string

// 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; }
}

Test code is: 测试代码为:

    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);
    }

which generates the following XML 生成以下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>

Your problem is that you have told it to deserialize to an object of type ParametersModel . 您的问题是您已经告诉它反序列化为ParametersModel类型的对象。 You have given no hints for names of elements so it is using the class name as the element name. 您没有提供元素名称的提示,因此它使用类名称作为元素名称。 If you were to change the xml so that the root element was ParametersModel then you would find it would no longer error. 如果您要更改xml,以使根元素为ParametersModel那么您将发现它不再出错。 It would however also not return the results you expected because of further similar problems elsewhere. 但是,由于其他地方存在类似的问题,它也不会返回您期望的结果。

In comments Dan suggested that it is easiest to start with serialization and he is correct. Dan在评论中建议最简单的方法是从序列化开始,他是正确的。 If you try to create an instance of your ParametersModel object and then serialize that then you will see the names and structures that your model suggests. 如果尝试创建ParametersModel对象的实例,然后对其进行序列化,则将看到模型建议的名称和结构。 If you then tweak your models and their attributes until the output matches what you are expecting you are then in a good position to correctly deserialize xml documents. 如果然后调整模型及其属性,直到输出与期望的结果匹配,则可以很好地正确反序列化xml文档。

您可能缺少XML标头标记,例如

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

Change the XmlRootAttribute declaration, on ParametersModel, from: 从以下参数更改XmlRootAttribute声明,在ParametersModel上:

[XmlRoot]

to: 至:

[XmlRoot(ElementName = "Parameters")]

Full posting: 完整发布:

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; }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM