如果你想导出一个文档及其所有后代,我建议制作一个子例程,给定一个文档和一个集合,将文档的孩子添加到集合中,然后循环遍历它的每个孩子并递归调用所述函数......或者如果它们不是父/响应文档,则无论其他逻辑显示“这是起始文档,添加相关文档”。然后,您可以导出集合。
没有办法只从导出器获取特定字段。
但是,如果您学习 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
可能是fieldname
或FIELDNAME
。起初我试图在我的第一个 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>
完毕!
这绝对是一个比您可能想要的更强大的答案。我希望它可以帮助你或其他人!