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

Why an XML string cannot be deserialized due to prefixes in root elements?

我有下面的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.

阅读了一些类似的帖子: 似乎 XmlSerializer.

可能存在错误

异常的原因是您将 stringReader 而不是 xmlReader 传递给 serializer.Deserialize()。您应该传递 XML reader 代替:

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) 将文本 reader 向前推进了一点,因此如果您稍后尝试直接从 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 = "")] to Instance to indicate the correct namespace for all child elements created from Instance. (Another option would be to add [XmlElement(Form = XmlSchemaForm.Unqualified)],但我认为在类型上设置单个属性更容易。)

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

  • 使用 StreamReader 直接从文件反序列化比先读入 string,然后使用 string 反序列化效率更高StringReader.

工作样本 fiddle here.