Create a gist now

Instantly share code, notes, and snippets.

Convert KeePass XML (2.x) format to keeper.txt format.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- convert KeePass XML (2.x) format to 7-column keeper.txt format -->
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/">
<xsl:apply-templates select="/KeePassFile/Root/Group" />
</xsl:template>
<xsl:template match="Group">
<xsl:param name="parent" select="'/'" />
<xsl:variable name="current" select="concat($parent, Name)" />
<!-- list password entries -->
<xsl:apply-templates select="Entry">
<xsl:with-param name="group" select="$current" />
</xsl:apply-templates>
<!-- list entries of subgroups recursively -->
<xsl:apply-templates select="Group">
<xsl:with-param name="parent" select="concat($current, '/')" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Entry">
<xsl:param name="group" />
<!-- pack Notes and multi-line custom fields into notes field -->
<xsl:variable name="notes">
<xsl:if test="String[Key='Notes']/Value!=''">
<xsl:call-template name="escape-ml">
<xsl:with-param name="value" select="String[Key='Notes']/Value" />
</xsl:call-template>
<xsl:text>\n\n</xsl:text>
</xsl:if>
<xsl:for-each select="String[contains(Value, '&#xA;') and
not(Key='Title' or Key='UserName' or Key='Password' or Key='Notes' or Key='URL')]">
<xsl:value-of select="concat('# ', Key, '\n')" />
<xsl:call-template name="escape-ml">
<xsl:with-param name="value" select="Value" />
</xsl:call-template>
<xsl:text>\n\n</xsl:text>
</xsl:for-each>
</xsl:variable>
<!-- pack single-line custom fields as JSON objects -->
<xsl:variable name="json">
<xsl:text>[</xsl:text>
<xsl:for-each select="String[not(contains(Value, '&#xA;')) and
not(Key='Title' or Key='UserName' or Key='Password' or Key='Notes' or Key='URL')]">
<xsl:if test="position()!=1">
<xsl:text>,</xsl:text>
</xsl:if>
<xsl:text>{"type":"text","name":</xsl:text>
<xsl:call-template name="to-json-str">
<xsl:with-param name="value" select="Key" />
</xsl:call-template>
<xsl:text>,"value":</xsl:text>
<xsl:call-template name="to-json-str">
<xsl:with-param name="value" select="Value" />
</xsl:call-template>
<xsl:text>}</xsl:text>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:variable>
<!-- put tab-separated fields: Title, UserName, Password, Notes, Group, URL, JSON -->
<xsl:value-of select="translate(String[Key='Title']/Value, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate(String[Key='UserName']/Value, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate(String[Key='Password']/Value, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate($notes, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate($group, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate(String[Key='URL']/Value, '&#x9;&#xA;', ' ')" />
<xsl:text>&#x9;</xsl:text>
<xsl:value-of select="translate($json, '&#x9;&#xA;', ' ')" />
<xsl:text>&#xA;</xsl:text>
</xsl:template>
<!-- escape multi-line text -->
<xsl:template name="escape-ml">
<xsl:param name="value" />
<xsl:call-template name="replace">
<xsl:with-param name="subject" select="$value" />
<xsl:with-param name="pattern" select="'&#xA;'" />
<xsl:with-param name="replace" select="'\n'" />
</xsl:call-template>
</xsl:template>
<!-- convert text to JSON string -->
<xsl:template name="to-json-str">
<xsl:param name="value" />
<xsl:text>"</xsl:text>
<xsl:call-template name="replace">
<xsl:with-param name="subject">
<xsl:call-template name="replace">
<xsl:with-param name="subject">
<xsl:call-template name="replace">
<xsl:with-param name="subject" select="$value" />
<xsl:with-param name="pattern" select="'\'" />
<xsl:with-param name="replace" select="'\\'" />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="pattern" select="'&#x9;'" />
<xsl:with-param name="replace" select="'\t'" />
</xsl:call-template>
</xsl:with-param>
<xsl:with-param name="pattern" select="'&quot;'" />
<xsl:with-param name="replace" select="'\&quot;'" />
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- replace $pattern in $subject with $replace -->
<xsl:template name="replace">
<xsl:param name="subject" />
<xsl:param name="pattern" />
<xsl:param name="replace" />
<xsl:choose>
<xsl:when test="contains($subject, $pattern)">
<xsl:value-of select="concat(substring-before($subject, $pattern), $replace)" />
<xsl:call-template name="replace">
<xsl:with-param name="subject" select="substring-after($subject, $pattern)" />
<xsl:with-param name="pattern" select="$pattern" />
<xsl:with-param name="replace" select="$replace" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$subject" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment