【问题标题】:Specify exact XML export structure in MS Access 2013在 MS Access 2013 中指定准确的 XML 导出结构
【发布时间】:2016-08-20 01:37:39
【问题描述】:

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

我需要导出的表是一个主要的Courses 表,以及其他几个直接相关和间接相关的表,例如Occurrences、OccurrencesUnits、Units、Locations、Staff、@987654329 @等

在我之前的Stack Overflow question 的帮助下(可能值得阅读一些背景信息),我能够设置所需的确切 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 表嵌套在它们的每个 Courses 条目中,但所有其他相关表需要在下面列出(不嵌套)。

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

我发现 accessforums.net 上的另一个人发现了类似的问题:http://www.accessforums.net/showthread.php?t=46933 他们的解决方案是删除 Access 中的表关系,公平地说,这确实会导致预期的 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)我需要的确切结构,即除 Occurrences 和 OccurrencesUnits 之外的Courses 列出的所有表外部?非常感谢您对此的任何支持!

2016 年 8 月 17 日添加:

为澄清起见,MS Access 2013 生成的 XML 如下所示。它与 2010 年类似,但问题是任何与 Courses 表直接相关的表都被省略了(CourseSubcategories、CourseTags、Partners、Staff 和 SubjectAreas ),并且包含它们的唯一方法是在 Access 中删除它们与 Courses 的关系。包括Units 和Locations 的表(因为它们与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 输出吗?它是否喜欢您发布的类似问题的链接,其中仅嵌套了其他表中的最后一条记录?
  • 现在已经在上面发布了当前的 XML 输出 - 希望对您有所帮助。抱歉,不确定您在第二个问题中的意思 - 我没有注意到表中的单个记录与该表中的其他记录表现不同的任何问题。
  • 这些表节点是否显示在原始 XML 中(转换前)?

标签: xml database ms-access xslt export


【解决方案1】:

考虑为那些未出现的相关表导出单独的临时 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>

【讨论】:

  • 太棒了,这行得通!不得不调整一些事情:我从“otherTables”对象中删除了几个表,并将Units和Locations添加到数组中,以与直接相关的表相同的方式处理它们,否则输出不正确形成。我还发现 setProperty 导致 VBA 错误,因此我将其更改为 rawDoc.SetProperty "AllowDocumentFunction", True。最后,在 XSLT 中,ancestor::Courses/ 选择不起作用,因此恢复为../../。现在完美运行 - 非常感谢您的帮助!
  • 确实,由于我没有您的数据库表,因此需要进行代码调整,我几乎为此添加了注释。 StackOverflow 上的其他 OP 期望完全完美的代码,否则您的答案是错误的!另外,我认为它应该是 ancestor::dataroot/ 首选的、更明确的 XPath 表达式。
猜你喜欢
  • 1970-01-01
  • 2019-12-06
  • 2016-12-24
  • 1970-01-01
  • 2013-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多