Какой самый быстрый способ программной проверки правильности XML-файлов в C#?
У меня есть большие партии файлов XHTML, которые обновляются вручную. Во время фазы обзора обновлений я хотел бы программно проверить правильность файлов. В настоящее время я использую XmlReader, но время, необходимое для среднего процессора, намного больше, чем я ожидал.
Размер файлов XHTML варьируется от 4 КБ до 40 КБ, и проверка занимает несколько секунд на файл. Проверка важна, но я бы хотел, чтобы время было как можно короче, поскольку проверка выполняется во время чтения файлов на следующем шаге процесса.
Есть ли более быстрый способ сделать простую проверку правильности XML? Может быть, с использованием внешних библиотек XML?
Я могу подтвердить, что проверка "обычного" содержимого на основе XML молниеносно выполняется с помощью XmlReader, и, как предполагается, проблема, по-видимому, связана с тем, что DTD XHTML читается при каждой проверке файла.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Обратите внимание, что в дополнение к DTD также загружаются соответствующие файлы.ent (xhtml-lat1.ent, xhtml-symbol.ent, xhtml-special.ent).
Поскольку полное игнорирование DTD на самом деле не вариант для XHTML, поскольку правильная формация тесно связана с допустимыми объектами HTML (например, & nbsp; будет быстро вводить ошибки проверки, когда мы игнорируем DTD).
Проблема была решена с помощью пользовательского XmlResolver, как предложено, в сочетании с локальными (встроенными) копиями файлов DTD и сущностей.
Я опубликую решение здесь, как только я очистил код
6 ответов
Я ожидаю, что XmlReader
с while(reader.Read)() {}
будет самый быстрый управляемый подход. Конечно, не нужно считывать секунды, чтобы прочитать 40 КБ... какой метод ввода вы используете?
Возможно, у вас есть какие-то внешние (схема и т. Д.) Сущности для разрешения? Если это так, вы можете написать XmlResolver
(устанавливается через XmlReaderSettings
) который использует локально кэшированные схемы, а не удаленную выборку...
Следующее делает ~300KB практически мгновенно:
using(MemoryStream ms = new MemoryStream()) {
XmlWriterSettings settings = new XmlWriterSettings();
settings.CloseOutput = false;
using (XmlWriter writer = XmlWriter.Create(ms, settings))
{
writer.WriteStartElement("xml");
for (int i = 0; i < 15000; i++)
{
writer.WriteElementString("value", i.ToString());
}
writer.WriteEndElement();
}
Console.WriteLine(ms.Length + " bytes");
ms.Position = 0;
int nodes = 0;
Stopwatch watch = Stopwatch.StartNew();
using (XmlReader reader = XmlReader.Create(ms))
{
while (reader.Read()) { nodes++; }
}
watch.Stop();
Console.WriteLine("{0} nodes in {1}ms", nodes,
watch.ElapsedMilliseconds);
}
Создать XmlReader
объект, передавая в XmlReaderSettings
объект, который имеет ConformanceLevel.Document
,
Это подтвердит правильность формирования.
Эта статья MSDN должна объяснить детали.
На моем довольно обычном ноутбуке чтение документа XML объемом 250 Кб от начала до конца с XmlReader
занимает 6 миллисекунд. Кое-что еще, кроме простого разбора XML, является виновником.
Я знаю, что я пишу некро, но я думаю, что это может быть решением
- используйте HTML Tidy, чтобы очистить ваш xml. установить опцию, чтобы удалить тип документа
- затем прочитайте сгенерированный xhtml/xml из tidy.
вот тот же код
public void GetDocumentStructure(int documentID)
{
string scmRepoPath = ConfigurationManager.AppSettings["SCMRepositoryFolder"];
string docFilePath = scmRepoPath + "\\" + documentID.ToString() + ".xml";
string docFilePath2 = scmRepoPath + "\\" + documentID.ToString() + "_clean.xml";
Tidy tidy = new Tidy();
tidy.Options.MakeClean = true;
tidy.Options.NumEntities = true;
tidy.Options.Xhtml = true;
// this option removes the DTD on the generated output of Tidy
tidy.Options.DocType = DocType.Omit;
FileStream input = new FileStream(docFilePath, FileMode.Open);
MemoryStream output = new MemoryStream();
TidyMessageCollection msgs = new TidyMessageCollection();
tidy.Parse(input, output, msgs);
output.Seek(0, SeekOrigin.Begin);
XmlReader rd = XmlReader.Create(output);
int node = 0;
System.Diagnostics.Stopwatch watch = System.Diagnostics.Stopwatch.StartNew();
while (rd.Read())
{
++node;
}
watch.Stop();
Console.WriteLine("Duration was : " + watch.Elapsed.ToString());
}
Лично я довольно ленивый... поэтому я ищу библиотеки.NET, которые уже решают проблему. Попробуйте использовать DataSet.ReadXML()
функционировать и ловить исключения. Это делает довольно удивительную работу по объяснению ошибок формата XML.
Как уже упоминалось, узким местом, скорее всего, является не XmlReader.
Проверьте, не случится ли вам много конкатенации строк без строителя строк.
Это действительно может нанести удар по вашей производительности.
Я использую эту функцию для проверки строк / фрагментов
<Runtime.CompilerServices.Extension()>
Public Function IsValidXMLFragment(ByVal xmlFragment As String, Optional Strict As Boolean = False) As Boolean
IsValidXMLFragment = True
Dim NameTable As New Xml.NameTable
Dim XmlNamespaceManager As New Xml.XmlNamespaceManager(NameTable)
XmlNamespaceManager.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema")
XmlNamespaceManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
Dim XmlParserContext As New Xml.XmlParserContext(Nothing, XmlNamespaceManager, Nothing, Xml.XmlSpace.None)
Dim XmlReaderSettings As New Xml.XmlReaderSettings
XmlReaderSettings.ConformanceLevel = Xml.ConformanceLevel.Fragment
XmlReaderSettings.ValidationType = Xml.ValidationType.Schema
If Strict Then
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ProcessInlineSchema)
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings)
Else
XmlReaderSettings.ValidationFlags = XmlSchemaValidationFlags.None
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.AllowXmlAttributes)
End If
AddHandler XmlReaderSettings.ValidationEventHandler, Sub() IsValidXMLFragment = False
AddHandler XmlReaderSettings.ValidationEventHandler, AddressOf XMLValidationCallBack
Dim XmlReader As Xml.XmlReader = Xml.XmlReader.Create(New IO.StringReader(xmlFragment), XmlReaderSettings, XmlParserContext)
While XmlReader.Read
'Read entire XML
End While
End Function
Я использую эту функцию для проверки файлов:
Public Function IsValidXMLDocument(ByVal Path As String, Optional Strict As Boolean = False) As Boolean
IsValidXMLDocument = IO.File.Exists(Path)
If Not IsValidXMLDocument Then Exit Function
Dim XmlReaderSettings As New Xml.XmlReaderSettings
XmlReaderSettings.ConformanceLevel = Xml.ConformanceLevel.Document
XmlReaderSettings.ValidationType = Xml.ValidationType.Schema
If Strict Then
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ProcessInlineSchema)
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.ReportValidationWarnings)
Else
XmlReaderSettings.ValidationFlags = XmlSchemaValidationFlags.None
XmlReaderSettings.ValidationFlags = (XmlReaderSettings.ValidationFlags Or XmlSchemaValidationFlags.AllowXmlAttributes)
End If
XmlReaderSettings.CloseInput = True
AddHandler XmlReaderSettings.ValidationEventHandler, Sub() IsValidXMLDocument = False
AddHandler XmlReaderSettings.ValidationEventHandler, AddressOf XMLValidationCallBack
Using FileStream As New IO.FileStream(Path, IO.FileMode.Open)
Using XmlReader As Xml.XmlReader = Xml.XmlReader.Create(FileStream, XmlReaderSettings)
While XmlReader.Read
'Read entire XML
End While
End Using
End Using
End Function