XmlReader не учитывает нотацию типа документа

Впервые пишу на этих форумах. Хотя давно их читаю.

У меня есть проблема, пытаясь проверить файл XML с XmlReader в.Net.

XML-файл:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
  <!NOTATION png PUBLIC "-//W3C//NOTATION Portable Network Graphics//EN">
  <!ENTITY mypic SYSTEM "mypic.png" NDATA png>
]>
<root>
  <img ref="mypic" />
</root>

XSD-файл:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="myschema"
    elementFormDefault="qualified"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="img">
    <xs:complexType>
      <xs:attribute name="ref" type="xs:ENTITY" />
    </xs:complexType>
  </xs:element>

  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="img" />
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Фрагмент C#:

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationEventHandler += settings_ValidationEventHandler;
settings.ValidationType = ValidationType.Schema;
settings.DtdProcessing = DtdProcessing.Parse;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessIdentityConstraints
            | XmlSchemaValidationFlags.ProcessInlineSchema
            | XmlSchemaValidationFlags.ProcessSchemaLocation
            | XmlSchemaValidationFlags.ReportValidationWarnings;

using (XmlReader reader = XmlReader.Create("myschema.xsd")) 
{
    settings.Schemas.Add(XmlSchema.Read(reader, new ValidationEventHandler(settings_ValidationEventHandler)));
}

using (XmlReader reader = XmlReader.Create("mydata.xml", settings))
{
    while (reader.Read()) ;
}

Я получаю сообщение об ошибке проверки:

Reference to an unparsed entity, 'mypic'.

В соответствии с другими валидаторами это действительно, но не в соответствии с XmlReader.

Я перепробовал все, что мог придумать, так что теперь я обращаюсь к вам, ребята. Буду признателен за любую оказанную помощь.

1 ответ

Решение

Используя Reflector, я обнаружил, что объявления NOTATION и ENTITY действительно анализируются, как и должны.

Валидатор найдет ваш mypic ссылка на сущность и рассматривать ее как внешнюю и непарсированную, поскольку она имеет непустое объявление NDATA.

Тем не менее, я также обнаружил, что валидатор всегда отправляет ссылку на ошибку проверки непарсированного объекта всякий раз, когда он встречает ссылку непарсированного объекта.

Я не понимаю, почему Microsoft сообщает об этом как об ошибке. По моему мнению, валидатор должен игнорировать неразобранные ссылки на сущности или сообщать о них как о предупреждениях, а не об ошибках.

Должно быть безопасно игнорировать эти ошибки из валидатора и все же считать XML допустимым, если, конечно, не сообщается о какой-либо другой ошибке.

Итак, как определить, является ли сообщаемая ошибка игнорируемой ссылкой на неразобранный объект - ошибка?

Я вижу три варианта:

  1. Проверьте, начинается ли сообщение об ошибке со строки Reference to an unparsed entity, Это сломается, если ваш код работает на неанглоязычной платформе.

  2. Используйте отражение, чтобы получить значение внутреннего GetRes свойство и посмотреть, равно ли оно Sch_UnparsedEntityRef, Это сломается, если Microsoft решит изменить внутренний API.

  3. Сериализировать исключение и определить, сериализован ли res член равен Sch_UnparsedEntityRef, Это сломается, если Microsoft решит изменить формат сериализации.

Все эти варианты являются "хаки". Первый, скорее всего, сломается. Третий, однако, должен быть безопасным. Маловероятно, что Microsoft изменяет формат сериализации, поскольку это может нарушить совместимость с другим кодом.

Вот примеры того, как вы можете определить, следует ли игнорировать исключение проверки, которое вы получаете в своем settings_ValidationEventHandler метод.

На основании сообщения об ошибке (не безопасно):

    static bool IsUnparsedEntityReferenceError_BasedOnMessage(
        XmlSchemaException error)
    {
        return error != null && error.Message.StartsWith(
            "Reference to an unparsed entity", StringComparison.Ordinal);
    }

Основано на рефлексии (вполне безопасно):

    static readonly PropertyInfo GetResProp = typeof(XmlSchemaException)
        .GetProperty("GetRes", BindingFlags.NonPublic | BindingFlags.Instance);

    static bool IsUnparsedEntityReferenceError_BasedOnReflection(
        XmlSchemaException error)
    {
        return error != null && GetResProp != null && 
            "Sch_UnparsedEntityRef".Equals(GetResProp.GetValue(error, null));
    }

На основе формата сериализации (самый безопасный):

    static bool IsUnparsedEntityReferenceError_BasedOnSerializer(
        XmlSchemaException error)
    {
        if (error == null)
        {
            return false;
        }
        else
        {
            SerializationInfo info = new SerializationInfo(
                typeof(XmlSchemaException), new FormatterConverter());

            error.GetObjectData(info, default(StreamingContext));
            return "Sch_UnparsedEntityRef" == info.GetString("res");
        }
    }
Другие вопросы по тегам