Доступ к VBA ImportXML метод импортирует только последнюю запись из файла XML - PullRequest
0 голосов
/ 04 сентября 2018

Я пытаюсь импортировать очень простой XML-файл в MS Access (2010), используя метод Application.ImportXML. Когда я просматриваю результаты импорта, таблица содержит только последнюю запись из файла XML.

Для начала - я создал небольшое веб-приложение ASP.NET (VB.NET) в Visual Studio 2017, которое имеет один контроллер PersonController.vb. Я оставил только методы по умолчанию, и у меня установлены текущие значения -

Public Function GetValues() As IEnumerable(Of String)
    Return New String() {"Person1", "Person2"}
End Function

Когда я запускаю это через IIS Express (Google Chrome), я могу просматривать результаты XML через Postmaster, как показано ниже -

["Person1","Person2"]

Или если я выберу json -

[
    "Person1",
    "Person2"
]

Я выбираю XML.

Так что теперь к моему Access VBA. Я создал новый модуль для проверки импорта XML в локальную таблицу доступа -

Option Compare Database
Option Explicit

Private Const URL As String = "http://localhost:58372/api/Person"
Private Const path As String = "C:/Users/my.name/Desktop/"

Public Enum RequestReadyState
    UNINITIALISED = 0
    LOADING
    LOADED
    INTERACTIVE
    COMPLETED
End Enum

Когда я вручную перехожу на http://localhost:58372/api/Person/, я получаю желаемый результат. У меня есть небольшая проблема со стороной VBA.

Примечание. Я добавил ссылку на библиотеку Microsoft XML, v6.0 для этого

Public Sub Readxml()
    Dim reader As New XMLHTTP60, doc As MSXML2.DOMDocument60

    With reader
    ' open the object
        .Open "GET", URL, False
        .setRequestHeader "Accept", "application/xml"
    ' send the command specified in the open
        .send

    ' wait until the request is complete, 4 is the completed state
    ' see (https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ms753800(v=vs.85))
        Do Until .ReadyState = RequestReadyState.COMPLETED
            DoEvents
        Loop

        ' check if we encountered an error
        ' status codes (https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)
        If .Status = 200 Then
            ' it worked
            Set doc = .responseXML
            doc.Save path & "Person.xml"
            Application.ImportXML path & "Person.xml", acStructureAndData
        Else
            ' it didn't
            MsgBox "It broke: " & .Status & " : " & .statusText
            Exit Sub
        End If
    End With

' clean up
    Set reader = Nothing
    If Not doc Is Nothing Then Set doc = Nothing
    Kill path & "Person.xml"
End Sub

Подпрограмма запускается до завершения без ошибок, однако, когда я открываю только что созданную таблицу ArrayOfstring, она содержит только 1 запись "Person2".

Я пытался решить это, но я потерян. doc.childNodes.length возвращает 1 (должно быть 2 записи?), Но я не понимаю, почему.

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

cvc-elt.1: Cannot find the declaration of element 'ArrayOfstring'.

Содержимое файла XML -

<ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <string>Person1</string>
  <string>Person2</string>
</ArrayOfstring>`

Может ли кто-нибудь указать мне правильное направление / заметить что-то очевидное, что я здесь упустил? Извините, если я немного прогулялся, просто немного запутался во всем этом.

Редактировать : я обновил ссылку на использование Microsoft XML v3.0 и обновил типы объектов, чтобы отразить новую ссылку, но я все еще получаю ту же проблему.

Edit2: Update : приведенный ниже ответ, подтверждающий преобразование в другой тип XML, работает, однако я решил использовать другое решение. Чтобы избежать необходимости использовать локальные таблицы, я настроил запрос заголовка на использование JSON, а затем разбираю его в коллекции для использования в приложении. Я обнаружил, что этот подход был более плавным для моей установки.

1 Ответ

0 голосов
/ 04 сентября 2018

Задача

Текущие результаты имеют смысл, поскольку миграция Access в XML отображает имена узлов в виде столбцов, а текстовые значения - в качестве значений строк, игнорируя любые атрибуты. В частности, ниже формат

<table_name>
  <col1>value1</col1>
  <col2>value2</col2>
</table_name>

становится:

table_name

  col1    col2
value1  value2

Однако, поскольку ваш XML-ответ содержит два одинаковых именованных узла (допустимо в XML), а таблицы базы данных не могут иметь одинаковые именованные столбцы, только последний экземпляр string сохраняется в качестве столбца.

Решение

Чтобы решить, после того, как вы загрузите XML с помощью запроса GET, рассмотрите возможность запуска XSLT, языка специального назначения для преобразования файлов XML, для преобразования узлов в string1 и string2 :

XSLT (сохранить как файл .xsl, специальный файл .xml)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                              xmlns:doc="http://schemas.microsoft.com/2003/10/Serialization/Arrays">

  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/doc:ArrayOfstring">
    <xsl:copy>
        <xsl:apply-templates select="doc:string"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="doc:string">
    <xsl:element name="{concat(local-name(), position())}" 
                 namespace="http://schemas.microsoft.com/2003/10/Serialization/Arrays"> 
        <xsl:value-of select="." />
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

* 1034 VBA *

Public Sub XMLResposeTransform()

On Error GoTo ErrHandle
    ' ADD MSXML, v6.0 REFERENCE UNDER TOOLS
    Dim xmlDoc As New MSXML2.DOMDocument60, xslDoc As New MSXML2.DOMDocument60, newDoc As New MSXML2.DOMDocument60

    ' LOAD XML AND XSL FILES
    xmlDoc.async = False
    xmlDoc.Load "Input.xml"

    xslDoc.async = False
    xslDoc.Load "XSLT_Script.xsl"

    ' TRANSFORM XML
    xmlDoc.transformNodeToObject xslDoc, newDoc
    newDoc.Save "Output.xml"

    Application.ImportXML "Output.xml", acStructureAndData

    MsgBox "Successfully imported the transformed XML!", vbInformation

ExitHandle:
    Set xmlDoc = Nothing: Set xslDoc = Nothing: Set newDoc = Nothing
    Exit Sub

ErrHandle:
    MsgBox Err.Number & " - " & Err.Description, vbInformation
    Resume ExitHandle

End Sub

Вывод XML

<?xml version="1.0" encoding="UTF-16"?>
<ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <string1>Person1</string1>
    <string2>Person2</string2>
</ArrayOfstring>

Таблица доступа

ArrayOfstring

string1    string2
Person1    Person2

Если ваш XML-файл имеет несколько элементов повторяющихся дочерних элементов, то будет распространяться несколько строк:

Гипотетический XML

<?xml version="1.0" encoding="UTF-16"?>
<ArrayOfstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <newtable>
    <string1>Person1</string1>
    <string2>Person2</string2>
  </newtable>
  <newtable>
    <string1>Person1</string1>
    <string2>Person2</string2>
  </newtable>
  <newtable>
    <string1>Person1</string1>
    <string2>Person2</string2>
  </newtable>
</ArrayOfstring>

Таблица доступа

newtable

string1    string2
Person1    Person2
Person1    Person2
Person1    Person2
...