/ / XML / XSLT / Access / VBA: jak połączyć wszystkie elementy podrzędne (nawet nieznane) w jeden przed zaimportowaniem do bazy danych Access? - xml, xslt, ms-access, vba

XML / XSLT / Access / VBA: jak mogę scalić wszystkie elementy potomne (nawet nieznane elementy) w jeden, przed importowaniem do bazy danych Access? - xml, xslt, ms-access, vba

BIEŻĄCY XML:

<?xml version="1.0"?>

<form1>
<page1>
<first_name></first_name>
<last_name></last_name>
.
.
</page1>
<page2>
<address></address>
<phone_number></phone_number>
.
.
</page2>
<page3>
<company_name></company_name>
<job_title></job_title>
.
.
</page3>
</form1>

ŻĄDANY XML - chcę scalić wszystkie elementy podrzędne i zmienić nazwę rodzica:

<?xml version="1.0"?>

<form>
<page>
<first_name></first_name>
<last_name></last_name>
.
.
<address></address>
<phone_number></phone_number>
.
.
<company_name></company_name>
<job_title></job_title>
.
.
</page>
</form>

wtedy, ponieważ mam tysiące plików XML z rozszerzeniemniektóre nieznane elementy, chcę znaleźć je wszystkie przed zbiorczym zaimportowaniem XML do bazy danych Access, ponieważ wszelkie nowe elementy w kolejnych plikach zostaną usunięte, jeśli nie są zdefiniowane w schemacie.

nie wszystkie elementy potomne są znane. nie wszystkie nazwy plików są znane.

Jak więc mogę sprawdzić wszystkie pliki pod kątem wszystkich elementów, wypełnić tabelę Access nimi wszystkimi, a następnie zbiorczo zaimportować wszystkie rekordy XML, aby dopasować je do żądanego schematu, jak pokazano powyżej?

EDYTOWAĆ:

ok, rozumiem - nie ma atrybutów. miałem na myśli wszystkie elementy potomne. dzięki za wskazanie tego Oded, zaktualizowałem pytanie o poprawki.

to jest kod VBA, którego używam w programie Access do zbiorczego importowania plików:

 Private Sub cmdImport_Click()
Dim strFile As String "Filename
Dim strFileList() As String "File Array
Dim intFile As Integer "File Number
Dim strPath As String " Path to file folder

strPath = "C:UsersMainDesktopXML-files"
strFile = Dir(strPath & "*.XML")

While strFile <> ""
"add files to the list
intFile = intFile + 1
ReDim Preserve strFileList(1 To intFile)
strFileList(intFile) = strFile
strFile = Dir()
Wend
"see if any files were found
If intFile = 0 Then
MsgBox "No files found"
Exit Sub
End If

"cycle through the list of files
For intFile = 1 To UBound(strFileList)
Application.ImportXML strPath & strFileList(intFile), acAppendData

Next intFile
MsgBox "Import Completed"

End Sub

mogę użyć arkusza stylów, aby przekształcić XML w taki sposób:

  For intFile = 1 To UBound(strFileList)
Application.TransformXML strPath & strFileList(intFile), _
"C:UsersMainDesktopstylesheet2.xslt", _
"C:UsersMainDesktoptemp.xml", True
Application.ImportXML "C:UsersMainDesktoptemp.xml", acAppendData
Next intFile

MsgBox "Import Completed"
End Sub

jednak nie łączy wszystkich elementów pliku w jedną tabelę. brakuje mi czegoś czy muszę zapisać listę zmiennych? lub utworzyć identyfikatory atrybutów?

EDYTOWAĆ: Z komentarzy

moje nazwy plików to 1.xml, 2.xml, 3.xml, 4.xml, itp. Ale jak powiedziałem, mają tysiące

Odpowiedzi:

0 dla odpowiedzi № 1

Załóżmy, że te dokumenty wejściowe:

1.xml

<form1>
<page1>
<first_name>D</first_name>
<last_name>E</last_name>
</page1>
<page2>
<address>F</address>
<phone_number>1</phone_number>
</page2>
<page3>
<company_name>G</company_name>
</page3>
</form1>

2.xml

<form2>
<page1>
<first_name>A</first_name>
</page1>
<page2>
<address>B</address>
</page2>
<page3>
<company_name>C</company_name>
<job_title>H</job_title>
</page3>
</form2>

Ten arkusz stylów:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kElementByName" match="/*/*/*" use="name()"/>
<xsl:param name="pMaxFileNumber" select="2"/>
<xsl:template match="/">
<xsl:variable name="vFieldsNames">
<xsl:call-template name="names">
<xsl:with-param name="pFrom" select="1"/>
<xsl:with-param name="pTo" select="$pMaxFileNumber"/>
<xsl:with-param name="pFieldsNames" select=""|""/>
</xsl:call-template>
</xsl:variable>
<form>
<xsl:call-template name="merge">
<xsl:with-param name="pFrom" select="1"/>
<xsl:with-param name="pTo" select="$pMaxFileNumber"/>
<xsl:with-param name="pFieldsNames" select="$vFieldsNames"/>
</xsl:call-template>
</form>
</xsl:template>
<xsl:template name="names">
<xsl:param name="pFrom"/>
<xsl:param name="pTo"/>
<xsl:param name="pFieldsNames"/>
<xsl:choose>
<xsl:when test="$pFrom = $pTo">
<xsl:value-of select="$pFieldsNames"/>
<xsl:apply-templates
select="document(concat($pFrom,".xml"),/)/*/*/*
[count(.|key("kElementByName",
name())[1])=1]
[not(contains($pFieldsNames,
concat("|",name(),"|")))]"
mode="names"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vNewTop"
select="floor(($pTo - $pFrom) div 2) + $pFrom"/>
<xsl:variable name="vNewFieldsNames">
<xsl:call-template name="names">
<xsl:with-param name="pFrom" select="$pFrom"/>
<xsl:with-param name="pTo" select="$vNewTop"/>
<xsl:with-param name="pFieldsNames"
select="$pFieldsNames"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="names">
<xsl:with-param name="pFrom" select="$vNewTop + 1"/>
<xsl:with-param name="pTo" select="$pTo"/>
<xsl:with-param name="pFieldsNames"
select="$vNewFieldsNames"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="pFrom"/>
<xsl:param name="pTo"/>
<xsl:param name="pFieldsNames"/>
<xsl:choose>
<xsl:when test="$pFrom = $pTo">
<page>
<xsl:apply-templates
select="document(concat($pFrom,".xml"),/)/*/*[1]/*[1]">
<xsl:with-param name="pFieldsNames"
select="$pFieldsNames"/>
</xsl:apply-templates>
</page>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vNewTop"
select="floor(($pTo - $pFrom) div 2) + $pFrom"/>
<xsl:call-template name="merge">
<xsl:with-param name="pFrom" select="$pFrom"/>
<xsl:with-param name="pTo" select="$vNewTop"/>
<xsl:with-param name="pFieldsNames" select="$pFieldsNames"/>
</xsl:call-template>
<xsl:call-template name="merge">
<xsl:with-param name="pFrom" select="$vNewTop + 1"/>
<xsl:with-param name="pTo" select="$pTo"/>
<xsl:with-param name="pFieldsNames" select="$pFieldsNames"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/*/*">
<xsl:param name="pFieldsNames"/>
<xsl:apply-templates select="*[1]">
<xsl:with-param name="pFieldsNames" select="$pFieldsNames"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/*/*/*" name="copy">
<xsl:param name="pFieldsNames"/>
<xsl:copy>
<xsl:value-of select="."/>
</xsl:copy>
<xsl:variable name="vName" select="concat("|",name(),"|")"/>
<xsl:apply-templates select="following::*[1]">
<xsl:with-param name="pFieldsNames"
select="concat(substring-before($pFieldsNames,
$vName),
"|",
substring-after($pFieldsNames,
$vName))"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="/*/*[last()]/*[last()]">
<xsl:param name="pFieldsNames"/>
<xsl:call-template name="copy"/>
<xsl:variable name="vName" select="concat("|",name(),"|")"/>
<xsl:call-template name="empty">
<xsl:with-param name="pFieldsNames"
select="substring(
concat(substring-before($pFieldsNames,
$vName),
"|",
substring-after($pFieldsNames,
$vName)),
2)"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="/*/*/*" mode="names">
<xsl:value-of select="concat(name(),"|")"/>
</xsl:template>
<xsl:template name="empty">
<xsl:param name="pFieldsNames"/>
<xsl:if test="$pFieldsNames!=""">
<xsl:element name="{substring-before($pFieldsNames,"|")}"/>
<xsl:call-template name="empty">
<xsl:with-param name="pFieldsNames"
select="substring-after($pFieldsNames,"|")"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Wydajność:

<form>
<page>
<first_name>D</first_name>
<last_name>E</last_name>
<address>F</address>
<phone_number>1</phone_number>
<company_name>G</company_name>
<job_title />
</page>
<page>
<first_name>A</first_name>
<address>B</address>
<company_name>C</company_name>
<job_title>H</job_title>
<last_name />
<phone_number />
</page>
</form>

Uwaga: Jeśli to wysadzi twoją pamięć, musisz podzielić to na dwa arkusze stylów: najpierw wypisz imiona; po drugie, scal. Jeśli nie możesz przekazać parametru Application.TransformXML, maksymalna liczba plików jest ustalona. Ponadto nie może być żadnej dziury: jeśli maksymalna liczba plików to 3, 2.xml nie można przegapić (to dlatego, że fn:document zgłasza błąd)

EDYTOWAĆ: Dla transformacji dwuprzebiegowej.

Ten arkusz stylów z dowolnymi danymi wejściowymi (nie używane):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="pMaxFileNumber" select="2"/>
<xsl:template match="/">
<form>
<xsl:call-template name="copy">
<xsl:with-param name="pFrom" select="1"/>
<xsl:with-param name="pTo" select="$pMaxFileNumber"/>
</xsl:call-template>
</form>
</xsl:template>
<xsl:template name="copy">
<xsl:param name="pFrom"/>
<xsl:param name="pTo"/>
<xsl:choose>
<xsl:when test="$pFrom = $pTo">
<page>
<xsl:copy-of
select="document(concat($pFrom,".xml"),/)/*/*/*"/>
</page>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMiddle"
select="floor(($pTo - $pFrom) div 2) + $pFrom"/>
<xsl:call-template name="copy">
<xsl:with-param name="pFrom" select="$pFrom"/>
<xsl:with-param name="pTo" select="$vMiddle"/>
</xsl:call-template>
<xsl:call-template name="copy">
<xsl:with-param name="pFrom" select="$vMiddle + 1"/>
<xsl:with-param name="pTo" select="$pTo"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Wydajność:

<form>
<page>
<first_name>D</first_name>
<last_name>E</last_name>
<address>F</address>
<phone_number>1</phone_number>
<company_name>G</company_name>
</page>
<page>
<first_name>A</first_name>
<address>B</address>
<company_name>C</company_name>
<job_title>H</job_title>
</page>
</form>

A ten arkusz stylów:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kElementByName" match="/*/*/*" use="name()"/>
<xsl:variable name="vElements"
select="/*/*/*[count(.|key("kElementByName",name())[1])=1]"/>
<xsl:template match="form">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="page">
<xsl:copy>
<xsl:apply-templates select="$vElements">
<xsl:with-param name="pContext" select="."/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/*/*">
<xsl:param name="pContext"/>
<xsl:element name="{name()}">
<xsl:value-of select="$pContext/*[name()=name(current())]"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Z wyjściem wstecznym jako wejściem, wynik:

<form>
<page>
<first_name>D</first_name>
<last_name>E</last_name>
<address>F</address>
<phone_number>1</phone_number>
<company_name>G</company_name>
<job_title></job_title>
</page>
<page>
<first_name>A</first_name>
<last_name></last_name>
<address>B</address>
<phone_number></phone_number>
<company_name>C</company_name>
<job_title>H</job_title>
</page>
</form>

0 dla odpowiedzi nr 2

Ten arkusz stylów tworzy opisany przez Ciebie wynik.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" />

<xsl:template match="/">
<!--generate standard document element and it"s child element-->
<form>
<page>
<!--Apply templates to children of document element"s, child element"s, children-->
<xsl:apply-templates select="/*/*/node()" />
</page>
</form>
</xsl:template>

<!--Identity template copies all content forward-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

Jeśli chcesz tylko skopiować elementy pod elementami strony, a nie jakikolwiek węzeł () (element, tekst, komentarz lub instrukcja przetwarzania), możesz zmienić XPATH z: /*/*/node() do: /*/*/*


0 dla odpowiedzi № 3

Jeśli naprawdę chcesz po prostu Kopiuj cała zawartość pliku page{N} pierwiastków, to ta transformacja jest prawdopodobnie najkrótsza:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="/">
<form>
<page>
<xsl:copy-of select="/*/*/node()"/>
</page>
</form>
</xsl:template>
</xsl:stylesheet>