为什么由于根元素中的前缀而无法反序列化XML字符串?

时间:2018-08-13 01:12:31

标签: c# .net xml xmlserializer xml-deserialization

我的XML如下:

<y:input xmlns:y='http://www.blahblah.com/engine/42'>
    <y:datas>
        <y:instance yclass='ReportPeriod' yid="report">
            <language yid='en'/>
            <threshold>0.6</threshold>
            <typePeriod>predefinedPeriod</typePeriod>
            <interval>month</interval>
            <valuePeriod>April</valuePeriod>
            <fund yclass="Fund">
                <name>K</name>
                <indexName>CAC40</indexName>
            </fund>
        </y:instance>
    </y:datas>
</y:input>

我正试图反序列化

[XmlRoot(ElementName="fund")]
public class Fund 
{
    [XmlElement(ElementName="name")]
    public string Name { get; set; }

    [XmlElement(ElementName="indexName")]
    public string IndexName { get; set; }

    [XmlAttribute(AttributeName="yclass")]
    public string Yclass { get; set; }
}

[XmlRoot(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
public class Instance 
{
    [XmlElement(ElementName="language")]
    public Language Language { get; set; }

    [XmlElement(ElementName="threshold")]
    public string Threshold { get; set; }

    [XmlElement(ElementName="typePeriod")]
    public string TypePeriod { get; set; }

    [XmlElement(ElementName="interval")]
    public string Interval { get; set; }

    [XmlElement(ElementName="valuePeriod")]
    public string ValuePeriod { get; set; }

    [XmlElement(ElementName="fund")]
    public Fund Fund { get; set; }

    [XmlAttribute(AttributeName="yclass")]
    public string Yclass { get; set; }

    [XmlAttribute(AttributeName="yid")]
    public string Yid { get; set; }
}

[XmlRoot(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public class Datas
{
    [XmlElement(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
    public Instance Instance { get; set; }
}

[XmlRoot(ElementName="input", Namespace="http://www.blahblah.com/engine/42")]
public class Input
{
    [XmlElement(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
    public Datas Datas { get; set; }

    [XmlAttribute(AttributeName="y", Namespace="http://www.blahblah.com/engine/42", Form = XmlSchemaForm.Qualified)]
    public string Y { get; set; }
}

但是,在反序列化上述XML时:

public static class Program
{
    public static void Main(params string[] args)
    {
        var serializer = new XmlSerializer(typeof(Input));
        using (var stringReader = new StringReader(File.ReadAllText("file.xml")))
        {
            using(var xmlReader = XmlReader.Create(stringReader))
            {
                var instance = (Input)serializer.Deserialize(stringReader);
            }
        }
    }
}

由于y前缀而导致错误...

There is an error in XML document (1, 1). ---> System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.

阅读类似这样的帖子:https://stackoverflow.com/a/36163079/4636721,看来XmlSerializer可能存在错误。

1 个答案:

答案 0 :(得分:2)

该异常的原因是您向stringReader传递了xmlReader而不是serializer.Deserialize()。您应该改为传递XML阅读器:

Input instance = null;
var serializer = new XmlSerializer(typeof(Input));
using (var stringReader = new StreamReader("file.xml"))
{
    using(var xmlReader = XmlReader.Create(stringReader))
    {
        instance = (Input)serializer.Deserialize(xmlReader);
    }
}   

(显然XmlReader.Create(stringReader)使文本阅读器稍微前进了一点,因此,如果您以后尝试直接从stringReader进行阅读,则它已移过根元素了。)

您的数据模型也有一些错误。看起来应该像这样:

[XmlRoot(ElementName="fund")]
public class Fund 
{
    [XmlElement(ElementName="name")]
    public string Name { get; set; }

    [XmlElement(ElementName="indexName")]
    public string IndexName { get; set; }

    [XmlAttribute(AttributeName="yclass")]
    public string Yclass { get; set; }
}

[XmlRoot(ElementName="instance")]
[XmlType(Namespace = "")] // Add this
public class Instance 
{
    [XmlElement(ElementName="language")]
    public Language Language { get; set; }

    [XmlElement(ElementName="threshold")]
    public string Threshold { get; set; }

    [XmlElement(ElementName="typePeriod")]
    public string TypePeriod { get; set; }

    [XmlElement(ElementName="interval")]
    public string Interval { get; set; }

    [XmlElement(ElementName="valuePeriod")]
    public string ValuePeriod { get; set; }

    [XmlElement(ElementName="fund")]
    public Fund Fund { get; set; }

    [XmlAttribute(AttributeName="yclass")]
    public string Yclass { get; set; }

    [XmlAttribute(AttributeName="yid")]
    public string Yid { get; set; }
}

[XmlRoot(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
public class Datas
{
    [XmlElement(ElementName="instance", Namespace="http://www.blahblah.com/engine/42")]
    public Instance Instance { get; set; }
}

[XmlRoot(ElementName="input", Namespace="http://www.blahblah.com/engine/42")]
public class Input
{
    [XmlElement(ElementName="datas", Namespace="http://www.blahblah.com/engine/42")]
    public Datas Datas { get; set; }

    //Remove This
    //[XmlAttribute(AttributeName="y", Namespace="http://www.blahblah.com/engine/42", Form = XmlSchemaForm.Qualified)]
    //public string Y { get; set; }
}

// Add this
[XmlRoot(ElementName="language")]
public class Language 
{
    [XmlAttribute(AttributeName="yid")]
    public string Yid { get; set; }
}

注意:

  • xmlns:y='http://www.blahblah.com/engine/42'XML namespace declaration,因此不应映射到数据模型中的成员。

  • <y:instance ...>的子元素不在任何命名空间中。除非子元素的命名空间由属性以某种方式指定,否则XmlSerializer将假定它们应与包含元素(此处为http://www.blahblah.com/engine/42")位于同一命名空间中。

    因此,有必要将[XmlType(Namespace = "")]添加到Instance,以指示从Instance创建的所有子元素的正确名称空间。 (另一种选择是向每个成员添加[XmlElement(Form = XmlSchemaForm.Unqualified)],但我认为在类型上设置单个属性会更容易。)

  • Language的定义未包含在您的问题中,因此我提出了一个定义。

  • 使用StreamReader直接从文件中反序列化比先读取string然后使用{{1 }}。

工作示例小提琴here