Как дифференцировать типы файлов XML перед десериализацией?
Я загружаю MusicXML-файлы в свою программу. Проблема: есть два "диалекта", по времени и по частям, которые имеют разные корневые узлы (и разную структуру):
<?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>
Мой код для десериализации частичной оценки:
using (var fileStream = new FileStream(openFileDialog.FileName, FileMode.Open))
{
var xmlSerializer = new XmlSerializer(typeof(ScorePartwise));
var result = (ScorePartwise)xmlSerializer.Deserialize(fileStream);
}
Как лучше всего провести различие между двумя диалектами?
2 ответа
Вот способ сделать это с помощью 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());
Я бы создал оба сериализатора
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%28v=vs.110%29.aspx
Если вас беспокоит использование ресурсов:
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\"?>";
}