Синтаксический анализ XML с пространствами имен с помощью F # XMLProvider

#xml #f# #xml-namespaces #type-providers

#xml #f# #xml-пространства имен #поставщики типов

Вопрос:

Я пытался проанализировать сообщение XML NLog с помощью XMLProvider. В моем проекте я определил поставщика следующим образом:

 XmlProvider<"""
    <log4j:event 
        logger="MyTest" level="INFO" timestamp="1597329370954" thread="1">
        <log4j:message>
            Running task
        </log4j:message>
        <log4j:locationInfo class="My.Task" method="Void main(System.String[])" />
        <nlog:eventSequenceNumber>
            11
        </nlog:eventSequenceNumber>
        <nlog:locationInfo assembly="My.Task, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        <nlog:properties />
        <log4j:properties>
            <data name="log4japp" value="My.Task(64400)" />
            <data name="log4jmachinename" value="BNT" />
        </log4j:properties>
    </log4j:event>""">
  

Но это не компилируется, поскольку я получаю исключения из-за отсутствия определений пространства имен.
Итак, я добавил xmlns к корневому элементу, сделав его:

 XmlProvider<"""
    <log4j:event 
        xmlns:log4j="http://jakarta.apache.org/log4j/"
        xmlns:nlog="http://www.nlog-project.org/schemas/NLog.xsd"
        logger="MyTest" level="INFO" timestamp="1597329370954" thread="1">
        <log4j:message>
           ...
        ...
    </log4j:event>""">
  

И это помогает моему проекту компилироваться, но во время выполнения при попытке проанализировать XML возникает исключение:

     'log4j' is an undeclared prefix. Line 1, position 2.
  

Я справился с проблемой, предварительно обработав xml и удалив все пространства имен xml из всех тегов. Хотя это работает, мне интересно, есть ли решение получше.

Комментарии:

1. У меня это работает.

Ответ №1:

У меня это работает нормально, но вам нужно включить объявления пространства имен XML как в образец XML, так и в фактические данные. Например, отлично работает следующее:

 // Simplified declaration with namespaces included
type X = XmlProvider<"""
  <log4j:event xmlns:log4j="http://jakarta.apache.org/log4j/"
        xmlns:nlog="http://www.nlog-project.org/schemas/NLog.xsd">
      <log4j:message>Running task</log4j:message>
      <nlog:eventSequenceNumber>11</nlog:eventSequenceNumber>
  </log4j:event>""">

// Load a sample data file with namespaces included
let x = X.Parse("""
<log4j:event xmlns:log4j="http://jakarta.apache.org/log4j/"
      xmlns:nlog="http://www.nlog-project.org/schemas/NLog.xsd">
    <log4j:message>Running task</log4j:message>
    <nlog:eventSequenceNumber>11</nlog:eventSequenceNumber>
</log4j:event>""")

// Returns the data from the XML just fine!
x.Message
x.EventSequenceNumber
  

Если вы опустите объявления пространства имен XML из данных, это приведет к сбою:

 let y = X.Parse("""
<log4j:event>
    <log4j:message>Running task</log4j:message>
    <nlog:eventSequenceNumber>11</nlog:eventSequenceNumber>
</log4j:event>""")
  

Я не думаю, что есть способ указать поставщику типов XML неявно включать объявления пространств имен — я вижу, насколько это было бы полезной функцией, поэтому не стесняйтесь вносить предложения в проект GitHub!

Комментарии:

1. Включение и выключение этого всегда было спорадической проблемой с тех пор, как …. по крайней мере, 15-20 лет — начиная с XmlDocument в эпоху VB6 и заканчивая различными технологиями xml (в .NET и т.д.). Это должно быть легко, но те кэши схемы и еще много чего, которые часто приходилось настраивать в прошлом, делали это утомительным. И все это время я задавался вопросом, возможно ли это без потери корректности, если реализация xml просто говорит: «есть префикс пространства имен — значит, есть пространство имен — но меня не волнует, что именно определяет это пространство имен». Учитывая, что проверка документа обычно была дополнительным шагом в любом случае.

2. @Tomas, спасибо — это именно тот случай, поскольку в моих фрагментах XML, которые я пытаюсь проанализировать, действительно отсутствуют определения пространств имен. Спасибо, что подтвердили, что это нелегко обойти.