为什么 XmlReader 将命名空间 uri 附加到每个元素?

发布于 2024-08-23 09:03:45 字数 2276 浏览 2 评论 0原文

我有一个包含以下格式的 xml 的流,我想将其反序列化为 C# 对象。

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<OrganisationMetaData xmlns="urn:organisationMetaDataSchema">
   <Organisations>
     <Organisation>
       <Code>XXX</Code>
       <Name>Yyyyyy</Name>...

我已经使用字符串完成了很多次,但对于该流,它会友好地将名称空间属性附加到所有复杂元素。如果我只是删除 xmlns 属性,并且忘记根据架构验证它,它只会附加一个空的 xmlns 属性。我遇到的问题是 XmlSerializer 中的 Deserialize 方法(?)抛出一个错误,指出它不需要该属性。我尝试使用 XmlRoot 和 XmlType 属性来装饰该类,但这并没有改变任何内容。

这是我想要反序列化的类

[XmlRoot(
   ElementName = "OrganisationMetaData", 
   Namespace = "urn:organisationMetaDataSchema")]
public class OrganisationMetaData
{
    public List<Organisation> Organisations { get; set; }
}

[XmlType(
   TypeName = "Organisation", 
   Namespace = "urn:organisationMetaDataSchema")]
public class Organisation
{
   public string Code {get; set;}

   public string Name {get; set;}
}

这是用于完成工作的方法

 public IList<Organisation> DeserializeOrganisations(Stream stream)
    {
        var serializer = new XmlSerializer(typeof(OrganisationMetaData));

        var mappingAssembly = //Resource in another assembly

        var schemas = new XmlSchemaSet();
        schemas.Add(
            "urn:organisationMetaDataSchema",
            XmlReader.Create(
                mappingAssembly.GetManifestResourceStream(
                    // An xml schema
                    )
                )
            );
        var settings = new XmlReaderSettings()
                           {
                               ValidationType = ValidationType.Schema,
                               Schemas = schemas,
                               ValidationFlags =
                     XmlSchemaValidationFlags.ReportValidationWarnings
                           };            

        settings.ValidationEventHandler += settings_ValidationEventHandler;
        var reader = XmlReader.Create(stream, settings);

        var metaData= (OrganisationMetaData)serializer.Deserialize(reader);
        return metaData.Organisations.ToList();
    }

我已经使用 DataContractSerializer 尝试过,但这带来了它自己的学习机会,所以如果有人可以帮助我解决我应该在属性中放入的内容让 XmlSerializer 工作,那就太好了。

任何帮助将不胜感激,谢谢。

I've got a Stream containing xml in the following format that I want to deserialize into C# objects

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<OrganisationMetaData xmlns="urn:organisationMetaDataSchema">
   <Organisations>
     <Organisation>
       <Code>XXX</Code>
       <Name>Yyyyyy</Name>...

I've done this loads of times with strings, but with the stream it is kindly appending the namespace attribute to all the complex elements. If I just remove the xmlns attribute, and forget about validating it against a schema, it just appends an empty xmlns attribute. The problem I have is that the Deserialize method in XmlSerializer (?), throws an error saying it doesn't expect the attribute. I have tried decorating the class with the XmlRoot and XmlType attributes but this didn't change anything.

Here's the class I want to deserialize into

[XmlRoot(
   ElementName = "OrganisationMetaData", 
   Namespace = "urn:organisationMetaDataSchema")]
public class OrganisationMetaData
{
    public List<Organisation> Organisations { get; set; }
}

[XmlType(
   TypeName = "Organisation", 
   Namespace = "urn:organisationMetaDataSchema")]
public class Organisation
{
   public string Code {get; set;}

   public string Name {get; set;}
}

Here's the method that is being used to do the work

 public IList<Organisation> DeserializeOrganisations(Stream stream)
    {
        var serializer = new XmlSerializer(typeof(OrganisationMetaData));

        var mappingAssembly = //Resource in another assembly

        var schemas = new XmlSchemaSet();
        schemas.Add(
            "urn:organisationMetaDataSchema",
            XmlReader.Create(
                mappingAssembly.GetManifestResourceStream(
                    // An xml schema
                    )
                )
            );
        var settings = new XmlReaderSettings()
                           {
                               ValidationType = ValidationType.Schema,
                               Schemas = schemas,
                               ValidationFlags =
                     XmlSchemaValidationFlags.ReportValidationWarnings
                           };            

        settings.ValidationEventHandler += settings_ValidationEventHandler;
        var reader = XmlReader.Create(stream, settings);

        var metaData= (OrganisationMetaData)serializer.Deserialize(reader);
        return metaData.Organisations.ToList();
    }

I've tried this using DataContractSerializer but that brings it's own oppotunities to learn, so if anyone could help with what I ought to be putting in the attributes to get XmlSerializer to work, it would be great.

Any help would be appreciated, thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桜花祭 2024-08-30 09:03:45

这里的关键是 [XmlRoot] 只能应用于根类型,例如类;如果您使用 List<> 作为根,它将无法工作 - 但我们可以使用 [XmlElement] 对其进行填充。我正在使用 Stream 方法(通过 Encoding.UTF8),但请注意,这并不是 IMO 问题的核心(根类型是):

[XmlRoot(Namespace="urn:organisationMetaDataSchema")]
public class Organisations
{
    private readonly List<Organisation> items = new List<Organisation>();
    [XmlElement("Organisation")]
    public List<Organisation> Items { get { return items; } }

}
public class Organisation
{
    public string Code { get; set; }
    public string Name { get; set; }
}
static class Program
{
    static void Main()
    {
        string xml = @"<?xml version='1.0' encoding='utf-8' standalone='yes'?><Organisations xmlns='urn:organisationMetaDataSchema'><Organisation><Code>XXXX</Code><Name>YYYYYYYY</Name></Organisation></Organisations>";
        XmlSerializer ser = new XmlSerializer(typeof(Organisations));
        using (Stream input = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            Organisations orgs = (Organisations)ser.Deserialize(input);
        }
    }
}

The key here is that the [XmlRoot] can only be applied to a root type such as a class; if you are using a List<> as the root it won't work - but we can shim that with [XmlElement]. I'm using the Stream approach (via Encoding.UTF8), but note that this isn't really the heart of the issue IMO (the root type is):

[XmlRoot(Namespace="urn:organisationMetaDataSchema")]
public class Organisations
{
    private readonly List<Organisation> items = new List<Organisation>();
    [XmlElement("Organisation")]
    public List<Organisation> Items { get { return items; } }

}
public class Organisation
{
    public string Code { get; set; }
    public string Name { get; set; }
}
static class Program
{
    static void Main()
    {
        string xml = @"<?xml version='1.0' encoding='utf-8' standalone='yes'?><Organisations xmlns='urn:organisationMetaDataSchema'><Organisation><Code>XXXX</Code><Name>YYYYYYYY</Name></Organisation></Organisations>";
        XmlSerializer ser = new XmlSerializer(typeof(Organisations));
        using (Stream input = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            Organisations orgs = (Organisations)ser.Deserialize(input);
        }
    }
}
灯角 2024-08-30 09:03:45

我最终更改了代码以使用数据契约序列化器,它在命名空间周围给了我更明显的错误,这使我能够拥有一个根据架构验证 xml 流的读取器,然后倒带流并在另一个反序列化的读取器中再次使用它xml。

阅读这个问题让我注意到一个问题,其中 xml 元素需要按字母顺序排列。我还发现,当反序列化我的类的枚举属性时,我需要要求它存在(毕竟它不可为空)。

然后,这导致了另一个错误,其中我有一个 xml 节点,其中省略了一些值(根据我的架构,可以),但是数据协定期望这些值按顺序排列,因此我必须明确指定这一点。

我最终得到了这样的数据成员属性,

[DataMember(
        Name = "MyEnumType", 
        EmitDefaultValue = false, 
        IsRequired = true, 
        Order = 3)] 
//Just assume I added this prop after my Code, and Name properties from above

感谢 Marc 花时间查看这个。

I ended up changing the code to use data contract serializer, it gave me more obvious errors around namespace that allowed me to have a reader that validated the xml stream against the schema, and then rewind the stream and use it again in another reader that deserialized the xml.

Reading this question alerted me to a gotcha where xml elements are required to be in alphabetical order. I also found that when deserializing a property of my class that was an enum, I need to require this to be present (it is not nullable after all).

This then caused another error where I had an xml node with some values omitted (ok by my schema), however the data contract expected these to be in sequence so I had to specify this explicitly.

I ended up with a data member attribute like this

[DataMember(
        Name = "MyEnumType", 
        EmitDefaultValue = false, 
        IsRequired = true, 
        Order = 3)] 
//Just assume I added this prop after my Code, and Name properties from above

Thanks to Marc for taking time to look at this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文