【问题标题】:How do I differentiate types of XML files before deserializing?在反序列化之前如何区分 XML 文件的类型?
【发布时间】:2014-05-14 19:19:08
【问题描述】:

我正在将 MusicXML 文件加载到我的程序中。问题:有两种“方言”,timewisepartwise,它们有不同的根节点(和不同的结构):

<?xml version="1.0" encoding='UTF-8' standalone='no' ?>
<!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 2.0 Partwise//EN" "http://www.musicxml.org/dtds/partwise.dtd">
<score-partwise version="2.0">
    <work>...</work>
    ...
</score-partwise>

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE score-timewise PUBLIC "-//Recordare//DTD MusicXML 2.0 Timewise//EN" "http://www.musicxml.org/dtds/timewise.dtd">
<score-timewise version="2.0">
   <work>...</work>
   ...
</score-timewise>

到目前为止,我用于反序列化 partwise 分数的代码是:

using (var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open))
{
    var xmlSerializer = new XmlSerializer(typeof(ScorePartwise));
    var result = (ScorePartwise)xmlSerializer.Deserialize(fileStream);
}

区分这两种方言的最佳方法是什么?

【问题讨论】:

  • xml 文件有多大?
  • 这真的取决于作品,Palestrina 的四个声音的平均主题歌大约有 12000 行/300 KB。一整部交响乐肯定不止这些。
  • 好的,我将文件的第 3 行加载到一个字符串中,然后执行 String.IndexOf() 以按时间或按时间搜索,然后您就知道您正在处理的文件类型和可以选择正确的序列化器。
  • 我不知道自然界中有多少(任何)分数时间文件,所以大多数系统这样做的一种方法就是假设分数部分。这似乎是一个逃避的答案,但我认为这几乎总是人们所做的。

标签: c# xml deserialization xmlserializer musicxml


【解决方案1】:

这是一种方法,使用XDocument 解析文件,读取根元素以确定类型,并将其读入您的序列化程序。

var xdoc = XDocument.Load(filePath);
Type type;
if (xdoc.Root.Name.LocalName == "score-partwise")
    type = typeof(ScorePartwise);
else if (xdoc.Root.Name.LocalName == "score-timewise")
    type = typeof(ScoreTimewise);
else
    throw new Exception();
var xmlSerializer = new XmlSerializer(type);
var result = xmlSerializer.Deserialize(xdoc.CreateReader());

【讨论】:

  • 加载整个 xml 文档只是为了检查第一行会有点慢,因为文件至少有 12000 行。
  • 无论如何,您都将通过反序列化来读取整个文件。读取它->检查第一行->将内存中的文件发送到反序列化器不会太糟糕(假设内存使用情况还不错;我假设文件在几十MB或更少,应该是很好)。
【解决方案2】:

我会创建两个序列化程序

var partwiseSerializer = new XmlSerializer(typeof(ScorePartwise));
var timewiseSerializer = new XmlSerializer(typeof(ScoreTimewise));

假设只有这两个,我会在一个上调用 CanDeserialize 方法

using (var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open))
{
  using (var xmlReader = XmlReader.Create(filStream))
  {
    if (partwiseSerializer.CanDeserialize(xmlReader))
    {
       var result = partwiseSerializer.Deserialize(xmlReader);
    }
    else
    {
       var result = timewiseSerializer.Deserialize(xmlReader);
    }
  }
}

显然,这只是一个想法。如果有更多选项或根据您的应用程序设计,我会使用更复杂的方法来调用 CanDeserialize,但我认为该方法是关键:

http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.candeserialize.aspx

XmlReader 类可以在这里找到:

http://msdn.microsoft.com/en-us/library/System.Xml.XmlReader(v=vs.110).aspx

【讨论】:

    【解决方案3】:

    如果您担心资源使用情况:

        internal const string NodeStart = "<Error ";
        public static bool IsErrorDocument(string xml)
        {
            int headerLen = 1;
            if (xml.StartsWith(Constants.XMLHEADER_UTF8))
            {
                headerLen += Constants.XMLHEADER_UTF8.Length;
            }
            else if (xml.StartsWith(Constants.XMLHEADER_UTF16))
            {
                headerLen += Constants.XMLHEADER_UTF16.Length;
            }
            else
            {
                return false;
            }
            if (xml.Length < headerLen + NodeStart.Length)
            {
                return false;
            }
            return xml.Substring(headerLen, NodeStart.Length) == NodeStart;
        }
    
    internal class Constants
    {
        public const string XMLHEADER_UTF16 = "<?xml version=\"1.0\" encoding=\"utf-16\"?>";
        public const string XMLHEADER_UTF8 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多