0

这里有人知道如何将父表单和响应表单导出到 xml 文件吗?我得到了这段代码,它获取父表单中的所有对象并将其导出到 xml,但它不访问响应记录。

我需要将记录从 Lotus Notes 导出到 xml 文件,我可以在其中选择应从父表单和响应表单中包含哪些字段。

Option Public
Option Declare
%INCLUDE "lsconst.lss" 


Sub Initialize

'This function creates XML files from Notes documents. The name of each XML file is the RepID of the database,
'plus the NoteID of the document. Therfore, we can find the XML file later for each doc.

'Constants
Const XML_FILE_FIELD = "XmlDocRenderFile"
Const XML_OUTPUT_DIR = "C:\Users\Administrator\Documents\Archive\"
Const XML_OUTPUT_ROOT1 = "Rep_"
Const XML_OUTPUT_ROOT2 = "_Note_"
Const XML_OUTPUT_SUFFIX = ".xml"
Const ERR_GENERAL = 1001

'Variables
Dim Sess As NotesSession
Dim Stream As NotesStream
Dim Exporter As NotesDXLExporter
Dim ThisDb As NotesDatabase
Dim SelectedDocs As NotesDocumentCollection
Dim OneDoc As NotesDocument
Dim XmlFilePath As String, NoteID As String, RepID As String

'Set up generic error handler.
On Error Goto ErrorReturn

'Get a Notes session, which we will use throughout this code. 
Set Sess = New NotesSession

 'Get the current database and its replica ID.
Set ThisDb = Sess.CurrentDatabase
RepID = ThisDb.ReplicaID

'Get the collection of documents that were selected by the user when this agent is invoked.
Set SelectedDocs = ThisDb.UnprocessedDocuments

'Create an XML exporter tool.
Set Exporter = Sess.CreateDxlExporter

 'Create an output stream that will receive XML. 
Set Stream = Sess.CreateStream

'Attach the stream as the output of the XML exporter.
Call Exporter.SetOutput (Stream)

'Create a loop that will process all the selected documents.
Set OneDoc = SelectedDocs.GetFirstDocument
While Not OneDoc Is Nothing


    'Get the Note ID of this document
    NoteID = OneDoc.NoteID

    'Make this document the input to the XML exporter.
    Call Exporter.SetInput (OneDoc)

    'Create the name of the XML output file.
    XmlFilePath = XML_OUTPUT_DIR + XML_OUTPUT_ROOT1 + RepID + XML_OUTPUT_ROOT2 + NoteID+ XML_OUTPUT_SUFFIX

    'Associate the XML output stream with the output file.
    Call Stream.Open(XmlFilePath)

    'Translate the doc into XML.
    Call Exporter.Process

    'Close the output file.
    Call Stream.Close

    'Write the name of the XML file into the document.
    Call OneDoc.ReplaceItemValue (XML_FILE_FIELD, XmlFilePath)

    'Save this document to svae our changes.
    Call OneDoc.Save(True, True, False)  

      'Get the next selected document.
  NextDoc:
    Set OneDoc = SelectedDocs.GetNextDocument(OneDoc)

  'End of loop on all selected documents.
  Wend

  NormalReturn:
  Exit Sub

  ErrorReturn:
   Msgbox "Problem.  Error message is: " & Error$, MB_ICONSTOP, "Error"
   Resume ErrorReturn2
  ErrorReturn2:
   Exit Sub

  End Sub

任何愿意提供帮助的人将不胜感激。谢谢!

4

2 回答 2

1

如果你想导出一个文档及其所有后代,我建议制作一个子例程,给定一个文档和一个集合,将文档的孩子添加到集合中,然后循环遍历它的每个孩子并递归调用所述函数......或者如果它们不是父/响应文档,则无论其他逻辑显示“这是起始文档,添加相关文档”。然后,您可以导出集合。

没有办法只从导出器获取特定字段。

但是,如果您学习 XSL,您可以做很多事情。

您的需求可能与我的完全不同,但我的目标是获取公司、产品和工厂文档的数据库,并获得如下数据结构:

    <ExportGrouped daddy="JSmart523">
        <COMPANY      CREATED_TIMESTAMP="somedate" LAST_UPDATE_TIMESTAMP="somedate" DOMINOUNID="12345678901234567890123456789012"
                        COMPANYNAME="Acme R Us" OLD_SYSTEM_ID="42" OTHERATTRIBUTES="...">
            <PRODUCT  CREATED_TIMESTAMP="somedate" LAST_UPDATE_TIMESTAMP="somedate" DOMINOUNID="12345678901234567890123456789012"
                        PRODUCTNAME="..." OTHERATTRIBUTES="..."/>
            <PRODUCT  CREATED_TIMESTAMP="somedate" LAST_UPDATE_TIMESTAMP="somedate" DOMINOUNID="12345678901234567890123456789012"
                        PRODUCTNAME="..." OTHERATTRIBUTES="..."/>
            <FACILITY CREATED_TIMESTAMP="somedate" LAST_UPDATE_TIMESTAMP="somedate" DOMINOUNID="12345678901234567890123456789012"
                        NAME="..." OTHERATTRIBUTES="..."/>
            <FACILITY CREATED_TIMESTAMP="somedate" LAST_UPDATE_TIMESTAMP="somedate" DOMINOUNID="12345678901234567890123456789012"
                        NAME="..." OTHERATTRIBUTES="..."/>
        </COMPANY>
    </ExportGrouped>

这意味着每个文档一个节点,字段名称作为属性(我省略了例外),并且在其父文档的节点中具有响应文档的节点。

我尝试只在一个 XSL 文件中执行此操作,但随后每个主文档都需要第二次通过数据库才能找到相关文档,这意味着我拥有的数据越多,处理 XSL 所需的时间就会成倍增长!相反,我将其分解为两个 xsl 文件:第 1 步得到了我想要的数据,然后第 2 步按照我想要的顺序组织了它。

但是,Lotus Notes 中的字段名称不区分大小写,这让我很头疼,所以,除了通过 1 和通过 2,...

Pass 0:将项目名称翻译为小写

虽然 Notes 中的字段名不区分大小写,但 XSL 区分大小写。对于任何给定的文档,字段名称FieldName可能是fieldnameFIELDNAME。起初我试图在我的第一个 XSL 文件中处理这个问题,但它使代码变得混乱和缓慢,所以我在我的进程中添加了一个“第零次”传递。我还借此机会删除了我不想要的节点,例如配置文档或任何位图,因为目的地不会存储它们。输入 XML 文件始终是整个 Notes 数据库(默认 NotesDXLExporter 选项,因此不包括设计),或者,当只是调试某些东西时,单个文档。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dxl="http://www.lotus.com/dxl" version="1.0">
  <xsl:output indent="no"/>
  <!-- Thanks to http://stackoverflow.com/questions/586231/how-can-i-convert-a-string-to-upper-or-lower-case-with-xslt for this method of using translate() to convert to lower case within xsl 1.0 -->
  <xsl:variable name="smallCase" select="'abcdefghijklmnopqrstuvwxyz'"/>
  <xsl:variable name="upperCase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
  <xsl:template match="*|text()|@*">
    <xsl:copy>
      <xsl:apply-templates select="*|text()|@*"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="dxl:item/@name">
    <xsl:attribute name="name">
      <xsl:value-of select="translate(.,$upperCase,$smallCase)"/>
    </xsl:attribute>
  </xsl:template>

  <!-- easily identifiable nodes we will not need -->
  <xsl:template match="dxl:document[@form='Configuration']"/>
  <xsl:template match="dxl:document[@form='Counter']"/>
  <xsl:template match="dxl:document[@form='Preference']"/>
  <xsl:template match="dxl:document[@form='Void']"/>

  <xsl:template match="dxl:databaseinfo|dxl:updatedby|dxl:revisions|dxl:notesbitmap|dxl:compositedata|dxl:launchsettings|dxl:item[dxl:rawitemdata]|dxl:embeddedcontrol"/>
</xsl:stylesheet>

通行证 2:除了结构之外的一切

我的第二个文件完成了大部分工作,创建了 COMPANY、PRODUCT 和 FACILITY 节点,但没有重新排列它们——它们仍然是兄弟姐妹,是根节点的直接子节点。(警告:我不必担心对响应的响应。您的代码可能会有所不同。)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dxl="http://www.lotus.com/dxl" version="1.0" exclude-result-prefixes="dxl">
  <xsl:output indent="no"/>

  <!-- / -->
  <xsl:template match="/">
    <xsl:element name="ExportUngrouped" namespace="">
      <xsl:apply-templates select="*"/>
    </xsl:element>
  </xsl:template>

 <!--
    /*
   Uncaught root element!
   If this template matches then we have a problem. The root node this XSL is meant to process is /dxl:database or /dxl:document
  -->
  <xsl:template match="/*" priority="0">
    <xsl:element name="badelement">
      <xsl:attribute name="Expected">/database or /document</xsl:attribute>
      <xsl:attribute name="ExpectedNamespace">http://www.lotus.com/dxl</xsl:attribute>
      <xsl:attribute name="ActualNodeName">
        <xsl:call-template name="XPathOfCurrentNode"/>
      </xsl:attribute>
      <xsl:attribute name="ActualNamespace">
        <xsl:value-of select="namespace-uri()"/>
      </xsl:attribute>
      <xsl:copy>
        <xsl:copy-of select="@*"/>
      </xsl:copy>
    </xsl:element>
  </xsl:template>

  <!-- dxl:database -->
  <xsl:template match="dxl:database">
    <xsl:attribute name="replicaid">
      <xsl:value-of select="@replicaid"/>
    </xsl:attribute>
    <!-- other stuff I wanted at the root database node -->
      <!--This is just an example, btw! -->
      <xsl:variable name="daddy" select="dxl:document[@form='FAQ']/dxl:item[@name='DocTitle'][. = 'WhosYourDaddy'][1]/../dxl:item[@name='DaddyName']"/>
    <xsl:if test="$daddy">
      <xsl:attribute name="daddy"><xsl:value-of select="$daddy"/></xsl:attribute>
    </xsl:if>
    <xsl:apply-templates select="dxl:document"/>
  </xsl:template>

  <!-- dxl:document nodes I want. I recommend making a spreadsheet that documents the forms you are looking for and generates the desired XSL. In my real XSL file, this wasn't pretty and indented, it was a copy & paste of one line per xsl:template tag. -->
  <xsl:template match="dxl:document[@form='Company']">
    <xsl:element name="COMPANY">
        <xsl:call-template name="DocumentElementContents"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="dxl:document[@form='Product']">
    <xsl:element name="PRODUCT">
        <xsl:call-template name="DocumentElementContents"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="dxl:document[@form='Factory']">
    <xsl:element name="FACILITY">
        <xsl:call-template name="DocumentElementContents"/>
    </xsl:element>
  </xsl:template>

  <!-- dxl:document nodes that we somehow missed -->
  <xsl:template match="dxl:document">
    <xsl:element name="uncaughtdocument">
      <xsl:attribute name="form">
        <xsl:value-of select="@form"/>
      </xsl:attribute>
      <xsl:call-template name="DocumentElementContents"/>
    </xsl:element>
  </xsl:template>

  <!--
    *************************************************
    Templates for AttributeItems mode
    Called by named template DocumentElementContents 
    AttributeItems mode adds attributes to the element that is created for a given dxl:document.
    Where possible, data is copied here.
    *************************************************
  -->

  <!-- AttributeItems dxl:noteinfo -->
  <xsl:template mode="AttributeItems" match="dxl:noteinfo">
    <xsl:attribute name="CREATED_TIMESTAMP">
      <xsl:value-of select="dxl:created/dxl:datetime"/>
    </xsl:attribute>
    <xsl:attribute name="LAST_UPDATE_TIMESTAMP">
      <xsl:value-of select="dxl:modified/dxl:datetime"/>
    </xsl:attribute>
    <xsl:attribute name="DOMINOUNID">
      <xsl:value-of select="@unid"/>
    </xsl:attribute>
  </xsl:template>

  <!--

    Called by DocumentElementContents.
    Describes what to do with an item when we are looking for items to include as attributes.
    I recommend making an Excel spreadsheet to generate this part because then your project documentation will be your code generator.
  -->
  <xsl:template mode="AttributeItems" match="dxl:document[@form='Company']/dxl:item[@name='CompanyName']">
    <xsl:attribute name="COMPANYNAME">
      <xsl:call-template name="AttributeSafeValueOfItem"/>
    </xsl:attribute>
  </xsl:template>

  <xsl:template mode="AttributeItems" match="dxl:document[@form='Company']/dxl:item[@name='CompanyID']">
    <xsl:attribute name="OLD_SYSTEM_ID">
      <xsl:call-template name="AttributeSafeValueOfItem"/>
    </xsl:attribute>
  </xsl:template>

  <!-- etc, etc -->

  <!-- If an item is not caught above while we are in AttributeItems mode, ignore it. -->
  <xsl:template mode="AttributeItems" match="dxl:item"/>

  <!--
    *************************************************
    Templates for ElementItems mode
    *************************************************
  -->

  <!--
    Called by DocumentElementContents.
    Describes what to do with an item when we are looking through the items to see what element nodes should be added
  -->
  <!-- Your code goes here for each element node to be created within the node created for that document. I omitted this part because it didn't help answer (my interpretation of) your question. -->

  <!-- If an item is not caught above while we are in ElementItems mode, ignore it. -->
  <xsl:template mode="ElementItems" match="dxl:item"/>

  <!--
    "DocumentElementContents"
    generic code to be called for each NotesDocument we are exporting.
  -->
  <xsl:template name="DocumentElementContents">
    <xsl:choose>
      <xsl:when test="@parent">
        <xsl:attribute name="MainDoc">false</xsl:attribute>
        <xsl:attribute name="ParentUNID">
          <xsl:value-of select="@parent"/>
        </xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
        <xsl:attribute name="MainDoc">true</xsl:attribute>
      </xsl:otherwise>
    </xsl:choose>
    <xsl:apply-templates mode="AttributeItems" select="dxl:item|dxl:noteinfo"/>
    <xsl:apply-templates mode="ElementItems" select="dxl:item"/>
  </xsl:template>

  <!-- 
    AttributeSafeValueOfItem
    Outputs the values of the item, delimited by commas if there are multiple values.
    Current node expected to be an item, but will work with any node where we want to export the content of all descendants that do not have the string 'list' in them. (e.g. item/textlist will be excluded but values within item/textlist/test will be exported)
  -->
  <xsl:template name="AttributeSafeValueOfItem">
    <xsl:for-each select=".//*[not(contains(local-name(),'list'))]">
      <xsl:value-of select="."/>
      <xsl:if test="position() != last()">, </xsl:if>
    </xsl:for-each>
  </xsl:template>

  <!--
    XPathOfCurrentNode
    For debugging. Outputs a string that is the XPath of the current node.
  -->
  <xsl:template name="XPathOfCurrentNode">
    <xsl:for-each select="ancestor-or-self::*">
      <xsl:call-template name="XPathOfCurrentNode_NodeLevel"/>
    </xsl:for-each>
  </xsl:template>

  <!--
    XPathOfCurrentNode_NodeLevel
    For debugging. Called by XPathOfCurrentNode for each ancestor-or-self::.
  -->
  <xsl:template name="XPathOfCurrentNode_NodeLevel">
    <xsl:variable name="precedingCount" select="count(preceding-sibling::*[name(.)=name(current())])"/>
    <xsl:text>/</xsl:text>
    <!--<xsl:value-of select="namespace-uri()"/>-->
    <xsl:value-of select="name(.)"/>
    <xsl:if test="$precedingCount or count(following-sibling::*[name(.)=name(current())])">
      <xsl:text>[</xsl:text>
      <xsl:value-of select="$precedingCount+1"/>
      <xsl:text>]</xsl:text>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

请注意标签exclude-result-prefixes中的属性,xsl:stylesheet这样dxl:它就不会在整个输出中出现。

关卡 3:重组!

在这一点上,我拥有了我需要的一切,只是顺序错误。您的最终结构可能不同,但对我而言,此时我需要做的就是在父节点中移动每个子节点,以便每个 COMPANY 节点包含每个相关的 PRODUCT 和 FACILITY 节点。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!--
After the previous xsl file has processed the code, we have all we need but it's not organized. It's in the order of documents received.
We want to group all of the nodes related to a case within the main doc node itself.
-->
  <xsl:output indent="yes"/>
  <xsl:key name="kSupportingDocsByParent" match="/ExportUngrouped/*[@ParentUNID and @MainDoc='false']" use="@ParentUNID"/>

  <xsl:template match="/ExportUngrouped" priority="1">
    <ExportGrouped>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select="*[@MainDoc='true']">
        <xsl:sort select="@ParentUNID"/>
      </xsl:apply-templates>
    </ExportGrouped>
  </xsl:template>

  <xsl:template match="/ExportUngrouped/*[@MainDoc='true']" priority="1">
    <xsl:copy>
      <xsl:apply-templates select="@*|*"/>
      <xsl:for-each select="key('kSupportingDocsByParent',@DOMINOUNID)">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="@MainDoc|@OtherAttributesTheFinalOutputShouldNotHave" priority="1"/>

  <xsl:template match="/ExportUngrouped/*/node()|@*" priority="0">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

完毕!

这绝对是一个比您可能想要的更强大的答案。我希望它可以帮助你或其他人!

于 2017-04-03T17:22:28.020 回答
0

您需要获取每个父文档的响应文档并遍历它们,以相同的方式导出它们。您甚至可能有响应到响应的文档,因此您可能需要编写递归函数。

于 2017-03-28T03:30:13.810 回答