在 MS Access 2013 中指定确切的 XML 导出结构

Specify exact XML export structure in MS Access 2013

我有一个包含课程数据的 MS Access 数据库,该数据库会定期导出到 XML 以为网站提供数据。

我需要导出的table是一个主要的Coursestable,加上其他几个直接相关和间接相关的table,例如Occurrences, OccurrencesUnits, Units, Locations, Staff, SubjectAreas, 等等

在我之前的 的帮助下(可能值得阅读一些背景信息)我能够设置所需的确切 XML 结构,下面进行了简化:

    <Courses>
            <CourseID>1</CourseID>
            <CourseTitle>Meat Science</CourseTitle>
            (etc etc)
            <Occurrences>
                    <OccurrenceID>1</OccurrenceID>
                    <OccurrenceTitle>Meat Science Autumn 2016</OccurrenceTitle>
                    <CourseID>1</CourseID>
                    <OccurrencesUnits>
                            <OccurrencesUnitsID>1</OccurrencesUnitsID>
                            <OccurrenceID>1</OccurrenceID>
                            <UnitID>1</UnitID>
                    </OccurrencesUnits>
            </Occurrences>
    </Courses>

    <Courses>
       ... more courses...
    </Courses>

    <Units>
       ...
    </Units>

    <Locations>
       ...
    </Locations>

    <Staff>
       ...
    </Staff>

    <SubjectAreas>
       ...
    </SubjectAreas>

    etc etc

所以有几层 Occurrences 然后 OccurrencesUnits table 嵌套在它们的每个 Courses 条目中,但是所有其他相关tables 需要列在下面(不嵌套)。

但是,在最近从 Access 2010 升级到 2013 时,我发现我无法生成相同的结构。 Access 2013 似乎考虑了 table 关系,默认情况下将任何 直接相关的 table 嵌套到主 table 节点中。通过 XSL 指定布局结构时(根据上面链接的我的相关 Stack Overflow 问题),这会导致 directly 相关 table 从 [=84= 中省略] 导出,与 2010 年不同,它们自动出现在 Courses.

下方

我发现 accessforums.net 上的另一个人发现了类似的问题:http://www.accessforums.net/showthread.php?t=46933 他们的解决方案是删除 Access 中的 table 关系,公平地说,这确实会导致预期的结果XML 输出,但可能对我的数据库相当不利!

有关信息,我在 Access 中的 VBA 导出代码如下所示:

Private Sub ExportCourseCatalogXML_Click()

  Dim rawDoc As Object, xslDoc As Object, newDoc As Object
  Dim xmlstr As String, xslstr As String
  Dim otherTables As AdditionalData

  Set otherTables = Application.CreateAdditionalData
  otherTables.Add "Occurrences"
  otherTables.Add "OccurrencesUnits"
  otherTables.Add "Units"
  otherTables.Add "Locations"
  otherTables.Add "CourseSubcategories"
  otherTables.Add "CourseTags"
  otherTables.Add "Partners"
  otherTables.Add "Staff"
  otherTables.Add "SubjectAreas"

  Application.ExportXML acExportTable, "Courses", "S:\Science\Biosciences\AATP\Database\xml\course-catalog.xml", _
             , , , , , , AdditionalData:=otherTables

  ' LOAD XML AND XSL FILES '
  xmlstr = "C:\path\to\course-catalog.xml"
  xslstr = "C:\path\to\structure.xsl"

  Set rawDoc = CreateObject("MSXML2.DOMDocument")
  Set xslDoc = CreateObject("MSXML2.DOMDocument")
  Set newDoc = CreateObject("MSXML2.DOMDocument")

  rawDoc.async = False
  rawDoc.Load xmlstr

  xslDoc.async = False
  xslDoc.Load xslstr

  ' TRANSFORM TO NEW XML '
  rawDoc.transformNodeToObject xslDoc, newDoc

  ' SAVE NEW XML FILE '
  newDoc.Save "C:\path\to\course-catalog.xml"

  MsgBox "Successfully exported XML.", vbInformation

End Sub

我的 structure.xsl 看起来像这样:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
           xmlns:od="urn:schemas-microsoft-com:officedata"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"               
           exclude-result-prefixes="od xsi">

<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

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

<xsl:template match="Courses">
  <Courses>
      <xsl:copy-of select="CourseID"/>
      <xsl:copy-of select="CourseTitle"/>
      (etc etc)
      <xsl:for-each select="Occurrences">
      <Occurrences>
         <xsl:copy-of select="*"/>
         <xsl:variable name="occid" select="occurrenceID"/>
         <xsl:copy-of select="../../OccurrencesUnits[CourseOccurrence=$occid]"/>
      </Occurrences>
      </xsl:for-each>        
  </Courses>
</xsl:template>

<xsl:template match="OccurrencesUnits"/>

</xsl:transform>

所以我的问题 - 是否可以指定(大概通过 XSL)我需要的确切结构,即所有 table 都列在 之外 Courses除了OccurrencesOccurrencesUnits?在此先感谢您对此的支持!

2016 年 8 月 17 日添加:

为了清楚起见,可以在下面看到 MS Access 2013 生成的 XML。它与 2010 年类似,但问题是任何 直接 与课程 table 相关的 table 都被省略了(CourseSubcategories,[ =31=、PartnersStaffSubjectAreas),要包含它们的唯一方法是在 Access 中删除它们与 Courses 的关系。 UnitsLocations的table被包括在内(因为它们没有直接关系Courses).

<Courses>
        <CourseID>1</CourseID>
        <CourseTitle>Meat Science</CourseTitle>
        (etc etc)
        <Occurrences>
                <OccurrenceID>1</OccurrenceID>
                <OccurrenceTitle>Meat Science Autumn 2016</OccurrenceTitle>
                <CourseID>1</CourseID>
                <OccurrencesUnits>
                        <OccurrencesUnitsID>1</OccurrencesUnitsID>
                        <OccurrenceID>1</OccurrenceID>
                        <UnitID>1</UnitID>
                </OccurrencesUnits>
        </Occurrences>
</Courses>

<Courses>
   ... more courses...
</Courses>

<Units>
   ...
</Units>

<Locations>
   ...
</Locations>

考虑为那些没有出现的相关表导出单独的临时 xml 文件。然后,使用 document() 函数将那些 xml 包含在 XSLT 转换中。 VBA 转换后将删除它们。这种方法中一个非常重要的设置是 XSLT (.xsl) 脚本必须与 XML 文件位于同一文件夹中,因为它会进行相关文件引用:

VBA

Private Sub ExportCourseCatalogXML_Click()

  Dim rawDoc As Object, xslDoc As Object, newDoc As Object
  Dim xmlstr As String, xslstr As String
  Dim otherTables As AdditionalData
  Dim temp As Variant

  Set otherTables = Application.CreateAdditionalData
  otherTables.Add "Occurrences"
  otherTables.Add "OccurrencesUnits"

  ' EXPORT MAIN XML 
  Application.ExportXML acExportTable, "Courses", "S:\Science\Biosciences\AATP\Database\xml\course-catalog.xml", _
             , , , , , , AdditionalData:=otherTables

  ' EXPORT TEMP XMLS
  For Each temp in Array("Units", "Locations", "CourseSubcategories", "CourseTags", "Partners", "Staff", "SubjectAreas")
      Application.ExportXML acExportTable, temp, "S:\Science\Biosciences\AATP\Database\xml\" & temp & ".xml" 
  Next temp 

  ' LOAD XML AND XSL FILES '
  xmlstr = "C:\path\to\course-catalog.xml"
  xslstr = "C:\path\to\structure.xsl"

  Set rawDoc = CreateObject("MSXML2.DOMDocument")
  Set xslDoc = CreateObject("MSXML2.DOMDocument")
  Set newDoc = CreateObject("MSXML2.DOMDocument")

  rawDoc.async = False
  rawDoc.setProperty "AllowDocumentFunction", True
  rawDoc.Load xmlstr

  xslDoc.async = False
  xslDoc.setProperty "AllowDocumentFunction", True
  xslDoc.Load xslstr

  ' TRANSFORM TO NEW XML '
  rawDoc.transformNodeToObject xslDoc, newDoc

  ' SAVE NEW XML FILE '
  newDoc.Save "C:\path\to\course-catalog.xml"

  ' DELETE TEMP XMLs
  For Each temp in Array("Units", "Locations", "CourseSubcategories", "CourseTags", "Partners", "Staff", "SubjectAreas")
      xmlfile = "S:\Science\Biosciences\AATP\Database\xml\" & temp & ".xml"
      If Len(Dir(xmlfile, vbDirectory)) > 0 Then Kill xmlfile
  Next temp 

  MsgBox "Successfully exported XML.", vbInformation

End Sub

XSLT (必须保存在与其他 .xml 文件相同的目录中)

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
           xmlns:od="urn:schemas-microsoft-com:officedata"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"               
           exclude-result-prefixes="od xsi">

<xsl:output version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:template match="/dataroot">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
    <xsl:copy-of select="document('CourseSubcategories.xml')/dataroot/CourseSubcategories"/>
    <xsl:copy-of select="document('CourseTags.xml')/dataroot/CourseTags"/>
    <xsl:copy-of select="document('Partners.xml')/dataroot/Partners"/>
    <xsl:copy-of select="document('Staff.xml')/dataroot/Staff"/>
    <xsl:copy-of select="document('SubjectAreas.xml')/dataroot/SubjectAreas"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Courses">
  <Courses>
      <xsl:copy-of select="CourseID"/>
      <xsl:copy-of select="CourseTitle"/>
      (etc etc)
      <xsl:for-each select="Occurrences">
      <Occurrences>
         <xsl:copy-of select="*"/>
         <xsl:variable name="occid" select="occurrenceID"/>
         <xsl:copy-of select="ancestor::dataroot/OccurrencesUnits[CourseOccurrence=$occid]"/>
      </Occurrences>
      </xsl:for-each>        
  </Courses>
</xsl:template>

<xsl:template match="OccurrencesUnits"/>

</xsl:transform>