基本的に次のような XSLT スタイルシートがあります。
<?xml version="1.0" encoding="utf-8" ?>
<!--
** DEV. NOTES:
** - NOT TESTED YET!
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- *********** Output type definition ************ -->
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="value" mode="values" />
<!-- ******************************************* VALUE MATCHER ******************************************** -->
<xsl:template match="value[@name = 'field_name' or @name = 'field_type' or @name = 'field_dim']" mode="values">
<xsl:variable name="this" select="@name" />
<!-- Create element for the Name node. -->
<xsl:if test="$this = 'field_name'">
<xsl:element name="Name">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:if>
<!-- *************************************************************** -->
<!-- Create element for the Type node. -->
<xsl:if test="$this = 'field_type'">
<!-- If the type is not a record type. -->
<xsl:if test="not(text() = 'record')">
<xsl:element name="Type">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:if>
<!-- Else if the Type is record type. -->
<xsl:if test="text() = 'record'">
<xsl:element name="Type">RECORD</xsl:element>
</xsl:if>
</xsl:if>
<!-- ********************************************************************* -->
<!-- Depth & Value for each sub-tree where parenting record is NOT a field -->
<xsl:if test="$this = 'field_dim' and parent::record[value[@name = 'field_type' and text() != 'record']]">
<xsl:choose>
<xsl:when test="text() = '0'">
<!-- A simple variable is allowed. -->
<xsl:element name="Depth">SIMPLE</xsl:element>
<xsl:element name="Value">{NS}</xsl:element>
</xsl:when>
<xsl:when test="text() = '1'">
<!-- The parameter has the type of Array<T>. -->
<xsl:element name="Depth">ARRAY</xsl:element>
<xsl:element name="Value">{{NS}}</xsl:element>
</xsl:when>
<xsl:when test="text() = '2'">
<!-- The parameter is some kind of hash table like: java.util.Map<String, Object> -->
<xsl:element name="Depth">MAP</xsl:element>
<xsl:element name="Value">
<xsl:element name="TKey">{NS}</xsl:element>
<xsl:element name="TValue">{NS}</xsl:element>
</xsl:element>
</xsl:when>
<!-- So far no other stuff is allowed. -->
<xsl:otherwise />
</xsl:choose>
</xsl:if>
<!-- ***************************************************************** -->
<!-- Depth & Value for each sub-tree where parenting record is a field -->
<xsl:if test="$this = 'field_dim' and parent::record[value[@name = 'field_type' and text() = 'record']]">
<xsl:choose>
<xsl:when test="text() = '0'">
<!-- A simple variable is allowed. -->
<xsl:element name="Depth">SIMPLE</xsl:element>
</xsl:when>
<xsl:when test="text() = '1'">
<!-- The parameter has the type of Array<T>. -->
<xsl:element name="Depth">ARRAY</xsl:element>
</xsl:when>
<!-- So far no other stuff is allowed. -->
<xsl:otherwise />
</xsl:choose>
<xsl:element name="Value">
<xsl:apply-templates />
</xsl:element>
</xsl:if>
</xsl:template>
<!-- ********************************************* RECORD MATCHER ********************************************* -->
<xsl:template match="record[ancestor::record[@name='sig_in' or @name='sig_out'] and value[@name = 'field_name']]">
<!-- XPath to the actual record item -->
<xsl:param name="path" />
<!-- Set the new path -->
<xsl:variable name="newpath">
<xsl:value-of select="concat($path,'/', value[@name = 'field_name']/text())" />
</xsl:variable>
<!-- XSL:ELEMENT to contain nested records within value tags, so we can handle Array&Map types uniformly. -->
<xsl:copy>
<xsl:attribute name="path">
<xsl:value-of select="$newpath" />
</xsl:attribute>
<xsl:apply-templates mode="values" select="value" />
<xsl:apply-templates>
<xsl:with-param name="path" select="$newpath" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="record[@path]">
<xsl:element name="Value">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<!-- ********* Match the inputs ********* -->
<xsl:template match="record[@name='sig_in']">
<Inputs name="sig_in">
<xsl:apply-templates>
<xsl:with-param name="path" select="'sig_in'" />
</xsl:apply-templates>
</Inputs>
</xsl:template>
<!-- ********* Match the outputs ********* -->
<xsl:template match="record[@name='sig_out']">
<Outputs name="sig_out">
<xsl:apply-templates>
<xsl:with-param name="path" select="'sig_out'" />
</xsl:apply-templates>
</Outputs>
</xsl:template>
<!-- *********** Container node for the IO records *********** -->
<xsl:template match="Values[descendant::record[@name='svc_sig']]">
<Values name="svc_sig">
<xsl:apply-templates select="descendant::record[@name='svc_sig']" />
</Values>
</xsl:template>
<!-- *** Process node *** -->
<xsl:template match="node()">
<xsl:param name="path" />
<xsl:apply-templates select="node()">
<xsl:with-param name="path" select="$path" />
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
次に、次のドキュメントで Java からこの変換を実行します。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Values version="2.0">
<value name="svc_type">flow</value>
<value name="svc_subtype">default</value>
<value name="svc_sigtype">java 3.5</value>
<record javaclass="com.wm.util.Values" name="svc_sig">
<record javaclass="com.wm.util.Values" name="sig_in">
<value name="node_type">record</value>
<value name="is_public">false</value>
<value name="field_type">record</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<array depth="1" name="rec_fields" type="record">
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">unformatted_email</value>
<value name="field_type">record</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<value name="rec_closed">true</value>
<value name="modifiable">true</value>
<value name="rec_ref">t00cc_emailresponder.data:email</value>
<array depth="1" name="rec_fields" type="record">
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">senders</value>
<value name="field_type">string</value>
<value name="field_dim">1</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<value name="is_soap_array_encoding_used">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">mailBoxNames</value>
<value name="field_type">string</value>
<value name="field_dim">1</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<value name="is_soap_array_encoding_used">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_usereditable">true</value>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">subject</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_usereditable">true</value>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">recvDate</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">body</value>
<value name="field_type">object</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
</array>
</record>
</array>
<value name="modifiable">true</value>
</record>
<record javaclass="com.wm.util.Values" name="sig_out">
<value name="node_type">record</value>
<value name="is_public">false</value>
<value name="field_type">record</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<array depth="1" name="rec_fields" type="record">
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">formatted_email</value>
<value name="field_type">record</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
<value name="rec_closed">true</value>
<value name="modifiable">true</value>
<value name="rec_ref">t00cc_emailresponder.data:formatted_email</value>
<array depth="1" name="rec_fields" type="record">
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">@id</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">sender</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">mailBoxName</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">subject</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">recv_date</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
<record javaclass="com.wm.util.Values">
<value name="node_type">record</value>
<value name="node_comment"/>
<record javaclass="com.wm.util.Values" name="node_hints">
<null name="field_usereditable"/>
<value name="field_largerEditor">false</value>
<value name="field_password">false</value>
</record>
<value name="is_public">false</value>
<value name="field_name">body</value>
<value name="field_type">string</value>
<value name="field_dim">0</value>
<value name="nillable">true</value>
<value name="form_qualified">false</value>
<value name="is_global">false</value>
</record>
</array>
</record>
</array>
<value name="modifiable">true</value>
</record>
</record>
<value name="node_comment">Service converts email from transport/email to fomatted email.
Email's sender and receiver will be normalized, unnecessary parts (added by outlook or any other mail client)
will be removed from email address.
Email's sent date will be converted to xml datetimeformat.
Email's body will be mapped from bytestream to string. (convertion happend earlier)
Input:
unformatted_email - type of email
Output:
formatted_email - type of formatted_email - converted, formatted email. Values in this format will be compatible with
waiting format fields of INM web service.
</value>
<value name="stateless">no</value>
<value name="caching">no</value>
<value name="prefetch">no</value>
<value name="cache_ttl">15</value>
<value name="prefetch_level">1</value>
<value name="template">t00cc_emailresponder_functional_mail_formatMailFields</value>
<value name="template_type">html</value>
<value name="audit_level">off</value>
<value name="check_internal_acls">no</value>
<value name="icontext_policy">$null</value>
<value name="system_service">no</value>
<value name="retry_max">0</value>
<value name="retry_interval">0</value>
<value name="svc_in_validator_options">none</value>
<value name="svc_out_validator_options">none</value>
<value name="auditoption">0</value>
<null name="auditfields_input"/>
<null name="auditfields_output"/>
<record javaclass="com.wm.util.Values" name="auditsettings">
<value name="document_data">0</value>
<value name="startExecution">false</value>
<value name="stopExecution">false</value>
<value name="onError">true</value>
</record>
<value name="pipeline_option">1</value>
<null name="originURI"/>
<value name="modifiable">true</value>
<value name="is_public">false</value>
</Values>
出力用に、XSLT は次の XML ドキュメントを生成します。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Values name="svc_sig">
<Inputs name="sig_in">
<record path="sig_in/unformatted_email">
<Name>unformatted_email</Name>
<Type>RECORD</Type>
<Depth>SIMPLE</Depth>
<Value/>
<record path="sig_in/unformatted_email/senders">
<Name>senders</Name>
<Type>string</Type>
<Depth>ARRAY</Depth>
<Value>{{NS}}</Value>
</record>
<record path="sig_in/unformatted_email/mailBoxNames">
<Name>mailBoxNames</Name>
<Type>string</Type>
<Depth>ARRAY</Depth>
<Value>{{NS}}</Value>
</record>
<record path="sig_in/unformatted_email/subject">
<Name>subject</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_in/unformatted_email/recvDate">
<Name>recvDate</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_in/unformatted_email/body">
<Name>body</Name>
<Type>object</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
</record>
</Inputs>
<Outputs name="sig_out">
<record path="sig_out/formatted_email">
<Name>formatted_email</Name>
<Type>RECORD</Type>
<Depth>SIMPLE</Depth>
<Value/>
<record path="sig_out/formatted_email/@id">
<Name>@id</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/sender">
<Name>sender</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/mailBoxName">
<Name>mailBoxName</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/subject">
<Name>subject</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/recv_date">
<Name>recv_date</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/body">
<Name>body</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
</record>
</Outputs>
</Values>
さて、結果は実際には正しいように見えますが、私を夢中にさせる小さな「バグ」が 1 つだけあります。別のレコード内にネストされたレコードを Value ノード内に移動することができないようです。つまり、次のようなものを期待しています。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Values name="svc_sig">
<Inputs name="sig_in">
<record path="sig_in/unformatted_email">
<Name>unformatted_email</Name>
<Type>RECORD</Type>
<Depth>SIMPLE</Depth>
<Value>
<record path="sig_in/unformatted_email/senders">
<Name>senders</Name>
<Type>string</Type>
<Depth>ARRAY</Depth>
<Value>{{NS}}</Value>
</record>
<record path="sig_in/unformatted_email/mailBoxNames">
<Name>mailBoxNames</Name>
<Type>string</Type>
<Depth>ARRAY</Depth>
<Value>{{NS}}</Value>
</record>
<record path="sig_in/unformatted_email/subject">
<Name>subject</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_in/unformatted_email/recvDate">
<Name>recvDate</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_in/unformatted_email/body">
<Name>body</Name>
<Type>object</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
</Value>
</record>
</Inputs>
<Outputs name="sig_out">
<record path="sig_out/formatted_email">
<Name>formatted_email</Name>
<Type>RECORD</Type>
<Depth>SIMPLE</Depth>
<Value>
<record path="sig_out/formatted_email/@id">
<Name>@id</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/sender">
<Name>sender</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/mailBoxName">
<Name>mailBoxName</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/subject">
<Name>subject</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/recv_date">
<Name>recv_date</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
<record path="sig_out/formatted_email/body">
<Name>body</Name>
<Type>string</Type>
<Depth>SIMPLE</Depth>
<Value>{NS}</Value>
</record>
</Value>
</record>
</Outputs>
</Values>
更新: - すべてのレコードには、すべてのフィールドを含む 1 つの値ノードが含まれている必要があります。したがって、テンプレートを適用するために xsl:element を配置すると、現在の状況では「悪い」個々のフィールドを独自の Value ノードに配置する値になります。問題は、これらのレコードを相互に深くネストできるようにはまだ進化していません。各レコードには、フィールドに似たすべてのレコードを含む 1 つの値ノードのみを含める必要があります。
私の最初の印象は、上記の XSLT のコメントにあるように、XSLT スクリプトを変更して xsl:element を作成するというものでしたが、その結果、レコード フィールドごとに Value ノードが作成されました。実際には配列にあります。
ありがとう、ジョーイ