简体   繁体   中英

C# XML deserialization - missing value - Why?

I have this XML

<?xml version="1.0" encoding="utf-8"?>
<office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" office:version="1.2" grddl:transformation="http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl">
    <office:meta>
        <meta:creation-date>2014-09-16T11:15:44.96</meta:creation-date>
        <meta:editing-duration>PT00H01M36S</meta:editing-duration>
        <meta:editing-cycles>2</meta:editing-cycles>
        <dc:date>2014-09-16T11:17:09.59</dc:date>
        <meta:document-statistic meta:object-count="24" />
        <meta:generator>OpenOffice.org/3.2$Win32 OpenOffice.org_project/320m12$Build-9483</meta:generator>
    </office:meta>
</office:document-meta>

Now I want to deserialize the XML.

I get the meta information like creation-date, date, and generator, but object-count is NULL instead of 24, and doc.version is also NULL, while it should be 1.2.
What am I doing wrong ?

OpenOffice.ODP.Meta.DocumentMeta mydoc = Tools.XML.Serialization.DeserializeXmlFromFile<OpenOffice.ODP.Meta.DocumentMeta>(@"D:\UserName\Tests\Presentation_1.odp\meta.xml");

string ver = mydoc.Version;    
Console.WriteLine(mydoc);
Console.WriteLine(ver);

Serialization class:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;


namespace Tools.XML
{


    // http://www.switchonthecode.com/tutorials/csharp-tutorial-xml-serialization
    // http://www.codeproject.com/KB/XML/xml_serializationasp.aspx
    public class Serialization
    {


        public static void SerializeToXml<T>(T ThisTypeInstance, string strFileNameAndPath)
        {
            SerializeToXml<T>(ThisTypeInstance, new System.IO.StreamWriter(strFileNameAndPath));
        } // End Sub SerializeToXml


        public static string SerializeToXml<T>(T ThisTypeInstance)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            string strReturnValue = null;

            SerializeToXml<T>(ThisTypeInstance, new System.IO.StringWriter(sb));

            strReturnValue = sb.ToString();
            sb = null;

            return strReturnValue;
        } // End Function SerializeToXml


        public static void SerializeToXml<T>(T ThisTypeInstance, System.IO.TextWriter tw)
        {
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

            using (System.IO.TextWriter twTextWriter = tw) 
            {
                serializer.Serialize(twTextWriter, ThisTypeInstance);
                twTextWriter.Close();
            } // End Using twTextWriter

            serializer = null;
        } // End Sub SerializeToXml


        public static T DeserializeXmlFromFile<T>(string strFileNameAndPath)
        {
            T tReturnValue = default(T);

            using (System.IO.FileStream fstrm = new System.IO.FileStream(strFileNameAndPath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) 
            {
                tReturnValue = DeserializeXmlFromStream<T>(fstrm);
                fstrm.Close();
            } // End Using fstrm

            return tReturnValue;
        } // End Function DeserializeXmlFromFile


        public static T DeserializeXmlFromEmbeddedRessource<T>(string strRessourceName)
        {
            T tReturnValue = default(T);

            System.Reflection.Assembly ass = System.Reflection.Assembly.GetExecutingAssembly();


            using (System.IO.Stream fstrm = ass.GetManifestResourceStream(strRessourceName)) 
            {
                tReturnValue = DeserializeXmlFromStream<T>(fstrm);
                fstrm.Close();
            } // End Using fstrm

            return tReturnValue;
        } // End Function DeserializeXmlFromEmbeddedRessource


        public static T DeserializeXmlFromStream<T>(System.IO.Stream strm)
        {
            System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
            T ThisType = default(T);

            using (System.IO.StreamReader srEncodingReader = new System.IO.StreamReader(strm, System.Text.Encoding.UTF8)) 
            {
                ThisType = (T)deserializer.Deserialize(srEncodingReader);
                srEncodingReader.Close();
            } // End Using srEncodingReader

            deserializer = null;

            return ThisType;
        } // End Function DeserializeXmlFromStream


        #if notneeded

        public static void SerializeToXML<T>(System.Collections.Generic.List<T> ThisTypeInstance, string strConfigFileNameAndPath)
        {
            System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Generic.List<T>));

            using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(strConfigFileNameAndPath)) {
                serializer.Serialize(textWriter, ThisTypeInstance);
                textWriter.Close();
            }

            serializer = null;
        }
        // SerializeToXML


        public static System.Collections.Generic.List<T> DeserializeXmlFromFileAsList<T>(string strFileNameAndPath)
        {
            System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(System.Collections.Generic.List<T>));
            System.Collections.Generic.List<T> ThisTypeList = null;

            using (System.IO.StreamReader srEncodingReader = new System.IO.StreamReader(strFileNameAndPath, System.Text.Encoding.UTF8)) {
                ThisTypeList = (System.Collections.Generic.List<T>)deserializer.Deserialize(srEncodingReader);
                srEncodingReader.Close();
            }

            deserializer = null;

            return ThisTypeList;
        }
        // DeserializeXmlFromFileAsList

        #endif

    } // End Class Serialization


} // End Namespace COR.Tools.XML

XML holder:

using System;
using System.Xml.Serialization;
using System.Collections.Generic;


namespace OpenOffice.ODP.Meta
{


    [XmlRoot(ElementName = "document-statistic", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
    public class cDocumentStatistic
    {
        [XmlAttribute(AttributeName = "object-count", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public string ObjectCount { get; set; }
    }


    [XmlRoot(ElementName = "meta", Namespace = "urn:oasis:names:tc:opendocument:xmlns:office:1.0")]
    public class Meta
    {
        [XmlElement(ElementName = "creation-date", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public string CreationDate { get; set; }

        [XmlElement(ElementName = "editing-duration", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public string EditingDuration { get; set; }

        [XmlElement(ElementName = "editing-cycles", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public string EditingCycles { get; set; }

        [XmlElement(ElementName = "date", Namespace = "http://purl.org/dc/elements/1.1/")]
        public string Date { get; set; }

        [XmlElement(ElementName = "document-statistic", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public cDocumentStatistic DocumentStatistic { get; set; }

        [XmlElement(ElementName = "generator", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0")]
        public string Generator { get; set; }
    }


    [XmlRoot(ElementName = "document-meta", Namespace = "urn:oasis:names:tc:opendocument:xmlns:office:1.0")]
    public class DocumentMeta
    {
        [XmlElement(ElementName = "meta", Namespace = "urn:oasis:names:tc:opendocument:xmlns:office:1.0")]
        public Meta Meta { get; set; }

        [XmlAttribute(AttributeName = "meta", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string _Meta { get; set; }

        [XmlAttribute(AttributeName = "office", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Office { get; set; }

        [XmlAttribute(AttributeName = "xlink", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Xlink { get; set; }

        [XmlAttribute(AttributeName = "dc", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Dc { get; set; }

        [XmlAttribute(AttributeName = "presentation", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Presentation { get; set; }

        [XmlAttribute(AttributeName = "ooo", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Ooo { get; set; }

        [XmlAttribute(AttributeName = "smil", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Smil { get; set; }

        [XmlAttribute(AttributeName = "anim", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Anim { get; set; }

        [XmlAttribute(AttributeName = "grddl", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Grddl { get; set; }

        [XmlAttribute(AttributeName = "officeooo", Namespace = "http://www.w3.org/2000/xmlns/")]
        public string Officeooo { get; set; }

        [XmlAttribute(AttributeName = "version", Namespace = "urn:oasis:names:tc:opendocument:xmlns:office:1.0")]
        public string Version { get; set; }

        [XmlAttribute(AttributeName = "transformation", Namespace = "http://www.w3.org/2003/g/data-view#")]
        public string Transformation { get; set; }
    }


}

Additional question:
Does anybody know what the duration format is ?

I ran your code and found that you need to make few changes to your file.

Please note that you need not specify the namespace for object_count and version as both their tags have their namespace specified in the start for version office is specified in the tag itself office:document-meta

same for object_count namespace meta is specified in the tag meta:document-statistic

Here is the updated one:

<?xml version="1.0" encoding="utf-8"?>
<office:document-meta 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:xlink="http://www.w3.org/1999/xlink" 
xmlns:dc="http://purl.org/dc/elements/1.1/" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 
xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" 
xmlns:ooo="http://openoffice.org/2004/office" 
xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" 
xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" 
xmlns:grddl="http://www.w3.org/2003/g/data-view#" 
xmlns:officeooo="http://openoffice.org/2009/office" 
version="1.2" 
grddl:transformation="http://docs.oasis-open.org/office/1.2/xslt/odf2rdf.xsl">
    <office:meta>
        <meta:creation-date>2014-09-16T11:15:44.96</meta:creation-date>
        <meta:editing-duration>PT00H01M36S</meta:editing-duration>
        <meta:editing-cycles>2</meta:editing-cycles>
        <dc:date>2014-09-16T11:17:09.59</dc:date>
        <meta:document-statistic object-count="24" />
        <meta:generator>OpenOffice.org/3.2$Win32 OpenOffice.org_project/320m12$Build-9483</meta:generator>
    </office:meta>
</office:document-meta>

Hope this helps :)

This is because the deserializer is buggy.
Because the standard says you can omit the attribute namespace if the element namespace is the same. On the other hand, that obviously doesn't mean you absolutely have to omit the attribute namespace just because the element namespace is the same.

See XML Spec and this .

The solution is to somehow set a root-namespace that is not the same as the containing namespace, but leave the actual root-namespace unaffected...

If one has a XmlElement, that's possible because you can set XmlRoot there if you have it in a separate class, and set another namespace in the class where it is actually referenced.

In code, that's simply:

[XmlRoot(ElementName = "document-statistic", Namespace = "")]
public class cDocumentStatistic
{
    [XmlAttribute(AttributeName = "object-count", Namespace = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0" )]
    public string ObjectCount { get; set; }
}

The solution for version is a bit more complex: since it's an attribute, you cannot set xmlroot in the serializer (whyever that is as it is, that is the actual bug)...

So you have to hack together a XmlTextReader that ignores the namespaces on version, but doesn't ignore them on the rest... That goes like this

    // https://stackoverflow.com/questions/870293/can-i-make-xmlserializer-ignore-the-namespace-on-deserialization
    // helper class to ignore namespaces when de-serializing
    public class NamespaceIgnorantXmlTextReader : System.Xml.XmlTextReader
    {
        public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader) : base(reader) { }

        public override string NamespaceURI
        {

            get
            { //return ""
                // string nam = base.Name;
                // Console.WriteLine(this.Name);
                // Console.WriteLine(this.LocalName);

                if (StringComparer.InvariantCultureIgnoreCase.Equals(this.Name, "office:version"))
                {
                    return "";
                }



                // Console.WriteLine(nam);
                return base.NamespaceURI;
            }
        }
    }

And then you still have to tell the serializer to actually use this abdomination

public static T DeserializeXmlFromStream<T>(System.IO.Stream strm)
{
    System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
    T ThisType = default(T);



    using (System.IO.StreamReader srEncodingReader = new System.IO.StreamReader(strm, System.Text.Encoding.UTF8)) 
    {
        // ThisType = (T)deserializer.Deserialize(srEncodingReader);

        using (NamespaceIgnorantXmlTextReader SpecialNamespaceReaderBecauseMicrosoftSucks = new NamespaceIgnorantXmlTextReader(srEncodingReader))
        {
            ThisType = (T)deserializer.Deserialize(SpecialNamespaceReaderBecauseMicrosoftSucks);
            SpecialNamespaceReaderBecauseMicrosoftSucks.Close();
        } // End Using SpecialNamespaceReaderBecauseMicrosoftSucks

        srEncodingReader.Close();
    } // End Using srEncodingReader

    deserializer = null;

    return ThisType;
} // End Function DeserializeXmlFromStream

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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