如何将Lotus Notes文档(父表单和响应表单)导出到XML [英] How to export Lotus Notes documents (both parent and response forms) to XML

查看:150
本文介绍了如何将Lotus Notes文档(父表单和响应表单)导出到XML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Anybody here knows how to export the parent and response forms to xml file? I got this code wherein it gets all the objects in the parent form and exports it to xml but it doesn't access the response records.

我需要的是将记录从Lotus Notes导出到xml文件,在这里我可以选择从父"和响应"表单中都包括哪些字段.

What I will be needing is to export records from Lotus Notes to xml file where I can choose which fields should be included from both Parent and Response forms.

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

任何愿意帮助的人将不胜感激.谢谢!

Anyone willing to help will be much appreciated. Thank you!

推荐答案

如果要导出文档及其所有后代,建议创建一个子例程,该例程将给定的文档和集合添加到文档的子对象中.集合,然后遍历它的每个子级,并递归调用该函数...或者如果它们不是父级/响应型文档,则无论其他逻辑如何表示这是入门文档,请添加相关文档".然后,您可以导出集合.

If you want to export a document and all of its descendants, I recommend making a subroutine that, given a document and a collection, adds the document's children to the collection and then loops through each of it's children and calls said function recursively... or if they aren't parent/response documents, whatever other logic that says "here's the starter document, add related documents." Then you can export the collection.

无法从导出器中仅获取特定字段.

There isn't a way to get only specific fields from the exporter.

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

However, if you learn XSL, you can do quite a bit.

您的需求可能与我的完全不同,但是我的目标是建立一个包含公司,产品和工厂文档的数据库,并获得一个像这样的数据结构:

Your needs might be completely different from mine, but my goal was to take a database of Company, Product, and Factory documents and get a data structure like:

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

表示每个文档一个节点,字段名称作为属性(我已经省略了例外),并且响应文档的节点位于其父文档的节点之内.

which meant one node per document, field names as attributes (with exceptions I've omitted), and having response documents' nodes within their parent documents' nodes.

我试图仅在一个XSL文件中执行此操作,但是随后每个主文档都需要第二遍数据库才能找到相关文档,这意味着我拥有的数据越多,处理XSL所花费的时间就越长.呈指数增长!相反,我将其分解为两个xsl文件:第1遍获得了我想要的数据,然后第2遍按了我想要的顺序组织了它.

I tried to do it in just one XSL file but then every main document required a second pass through the database to find the related documents, which meant that the more data I had, the length of time it took to process the XSL grew exponentially! Instead I broke it up into two xsl files: Pass 1 got the data I wanted, then pass 2 organized it in the order I wanted.

但是,字段名称在Lotus Notes中并不区分大小写,这让我很头疼,因此,除了传递1和传递2之外,...

However, field names aren't case sensitive in Lotus Notes and that smacked me in the head, so, in addition to pass 1 and pass 2, ...

虽然在Notes中字段名称不区分大小写,但XSL区分大小写.对于任何给定的文档,字段名称FieldName可能是fieldnameFIELDNAME.最初,我试图在我的第一个XSL文件中处理此问题,但它使代码混乱且缓慢,因此我在过程中添加了"zeroth"通行证.我还趁机删除了我不想要的节点,例如配置文档或任何位图,因为目标位置不会存储它们.输入的XML文件始终是整个Notes数据库(默认的NotesDXLExporter选项,因此不包含设计),或者,在调试某些内容时,始终是一个文档.

While field names are not case sensitive in Notes, XSL is case sensitive. For any given document, field name FieldName might be fieldname or FIELDNAME. At first I tried to handle this within my first XSL file but it made the code messy and slow, so I added a "zeroth" pass to my process. I also took the opportunity to remove nodes I didn't want, such as config documents or any bitmaps since the destination wasn't going to store them. The input XML file was always the entire Notes database (default NotesDXLExporter options so design wasn't included) or, when just debugging something, a single document.

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

Pass 2:除了结构以外

我的第二个文件完成了大部分工作,创建了COMPANY,PRODUCT和FACILITY节点,但没有重新排列它们-它们仍然是同级节点,是根节点的直接子级. (注意:我不必担心响应的响应.您的代码可能有所不同.)

Pass 2: All But Structure

My second file did most of the work, creating the COMPANY, PRODUCT, AND FACILITY nodes, but not rearranging them - they were still siblings, direct children of the root node. (Caveat: I didn't have to worry about responses to responses. Your code might differ.)

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

请注意xsl:stylesheet标记中的exclude-result-prefixes属性,以便在整个输出中不会使用dxl:.

Note the exclude-result-prefixes attribute in the xsl:stylesheet tag so that dxl: isn't peppered throughout the output.

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

At this point I had everything I needed except it was in the wrong order. Your final structure may differ, but for me, all I needed to do at this point was to move each child node within the parent node so that each COMPANY node contained each relevant PRODUCT and FACILITY node.

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

完成!

这绝对是比您想要的答案更强大的答案.希望对您或其他人有帮助!

Done!

This is definitely a beefier answer than you had probably wanted. I hope it helps you or someone else!

这篇关于如何将Lotus Notes文档(父表单和响应表单)导出到XML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆