XML / XSLT / Access / VBA: как объединить все дочерние элементы (даже неизвестные элементы) в один перед импортом в базу данных Access? - PullRequest
1 голос
/ 12 сентября 2010

ТЕКУЩИЙ 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>

желаемый XML - я хочу объединить все дочерние элементы и переименовать родительский элемент:

<?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>

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

не все дочерние элементы известны. не все имена файлов известны.

Итак, как я могу проверить все файлы на наличие всех элементов, заполнить ими таблицу Access, а затем массово импортировать все записи XML, чтобы вписаться в желаемую схему, как показано выше?

EDIT:

хорошо, я вижу - нет атрибутов. Я имел в виду все дочерние элементы. спасибо за указание на это Одед, я обновил вопрос с исправлениями.

это код VBA, который я использую в Access для массового импорта файлов:

 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:\Users\Main\Desktop\XML-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

я могу использовать таблицу стилей для преобразования XML следующим образом:

  For intFile = 1 To UBound(strFileList)
     Application.TransformXML strPath & strFileList(intFile), _
     "C:\Users\Main\Desktop\stylesheet2.xslt", _
     "C:\Users\Main\Desktop\temp.xml", True
     Application.ImportXML "C:\Users\Main\Desktop\temp.xml", acAppendData
   Next intFile

 MsgBox "Import Completed"
End Sub

однако, он не объединяет все элементы файла в одну таблицу. я что-то пропустил? мне нужно сохранить список переменных? или создать какой-либо атрибут атрибутов?

РЕДАКТИРОВАТЬ : Из комментариев

мои имена файлов: 1.xml, 2.xml, 3.xml, 4.xml и т. Д. Но, как я уже сказал, тысячи

Ответы [ 3 ]

0 голосов
/ 12 сентября 2010

Если вы действительно хотите просто скопировать все содержимое page{N} элементов, то это преобразование, вероятно, самое короткое :

<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>
0 голосов
/ 14 сентября 2010

Предположим, это входные документы:

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>

Эта таблица стилей:

<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>

Вывод:

<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>

Примечание : если это взрывает вашу память, то вам нужно разделить ее на две таблицы стилей: во-первых,выводим имена;во-вторых, объединить.Если вы не можете передать параметр с Application.TransformXML, то максимальное количество файлов фиксировано.Кроме того, не должно быть никаких пробелов: если максимальное количество файлов равно 3, 2.xml не может быть пропущено (это потому, что fn:document выдает ошибку)

EDIT :Для двухпроходного преобразования.

Эта таблица стилей с любым входом (не используется):

<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>

Вывод:

<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>

И эта таблица стилей:

<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>

При выводе предиума в качестве входных данных результат:

<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 голосов
/ 12 сентября 2010

Эта таблица стилей создает результат, который вы описали.

<?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>

Если вы просто хотите скопировать элементы под элементами страницы, а не какой-либо узел () (элемент, текст, комментарий или инструкция обработки), вы можете изменить XPATH с: /*/*/node() до: /*/*/*

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...