开发者

Why is XmlReader appending namespace uris to each element?

开发者 https://www.devze.com 2022-12-22 02:01 出处:网络
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\"?>

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()
                           {
               开发者_JAVA技巧                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.


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


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号