XSLT-преобразование, создающее новые имена из элементов - PullRequest
2 голосов
/ 30 декабря 2010

Мне нужно преобразовать таблицу данных, которая поступает в выходные данные XML, как показано ниже. C1 столбец 1 c2 столбец2 и т. Д.

<?xml version="1.0" encoding="UTF-8"?>
<report>
    <report_header>
        <c1>desc</c1>
        <c2>prname</c2>
        <c3>prnum</c3>
        <c4>cdate</c4>
        <c5>phase</c5>
        <c6>stype</c6>
        <c7>status</c7>
        <c8>parent</c8>
        <c9>location</c9>
    </report_header>
    <report_row>
        <c1></c1>
        <c2>IT Project Message Validation</c2>
        <c3>IT-0000021</c3>
        <c4>12/14/2010 09:56 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
    <report_row>
        <c1></c1>
        <c2>David, Michael John Morning QA Test</c2>
        <c3>IT-0000020</c3>
        <c4>12/14/2010 08:12 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
</report>

в

<?xml version="1.0" encoding="UTF-8"?>
<report>
    <report_row>
        <desc></desc>
        <prname>IT Project Message Validation</prname>
        <prnum>IT-0000021</prnum>
        <cdate>12/14/2010 09:56 AM</cdate>
        <phase>Preparation</phase>
        <stype>IT Projects</stype>
        <status>Active</status>
        <parent>IT</parent>
        <location>/IT/BIOMED</location>
    </report_row>
    <report_row>
        <desc></desc>
        <prname>David, Michael John Morning QA Test</prname>
        <prnum>IT-0000020</prnum>
        <cdate>12/14/2010 08:12 AM</cdate>
        <phase>Preparation</phase>
        <stype>IT Projects</stype>
        <status>Active</status>
        <parent>IT</parent>
        <location>/IT/BIOMED</location>
    </report_row>
</report>

мой текущий xslt выглядит так

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:exslt="http://exslt.org/common">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
    <report>
        <xsl:apply-templates select="/report/report_row"/>          
    </report>
</xsl:template>

<xsl:template match="/report/report_row">
<report_row>
    <xsl:apply-templates select="c1"/>
    <xsl:apply-templates select="c2"/>
    <xsl:apply-templates select="c3"/>
    <xsl:apply-templates select="c4"/>
    <xsl:apply-templates select="c5"/>
    <xsl:apply-templates select="c6"/>
    <xsl:apply-templates select="c7"/>
    <xsl:apply-templates select="c8"/>
    <xsl:apply-templates select="c9"/>
</report_row>
</xsl:template> 

<xsl:template match="c1">   
    <xsl:element name="{/report/report_header/c1}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>
<xsl:template match="c2">   
    <xsl:element name="{/report/report_header/c2}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template> 
<xsl:template match="c3">   
    <xsl:element name="{/report/report_header/c3}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>
<xsl:template match="c4">   
    <xsl:element name="{/report/report_header/c4}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template> 
<xsl:template match="c5">   
    <xsl:element name="{/report/report_header/c5}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template> 
<xsl:template match="c6">   
    <xsl:element name="{/report/report_header/c6}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>
<xsl:template match="c7">   
    <xsl:element name="{/report/report_header/c7}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>
<xsl:template match="c8">   
    <xsl:element name="{/report/report_header/c8}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>
<xsl:template match="c9">   
    <xsl:element name="{/report/report_header/c9}"><xsl:value-of select="current()"/></xsl:element>
</xsl:template>

</xsl:transform>

Мое преобразование работает, если я предполагаю, что максимальное количество столбцов и заголовки столбцов могут быть допустимыми qnames.

Он начал давать сбои, когда я получил более 100 пределов, которые я принял, и заголовки столбцов с пробелами в них.

Как создать преобразование, в котором вместо этого используются подстановочные знаки, и как убрать пробелы и недопустимые символы из заголовков столбцов, чтобы сделать их допустимыми именами q *

Спасибо

Ответы [ 4 ]

4 голосов
/ 31 декабря 2010

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:variable name="vAlphanum" select=
  "concat('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
          'abcdefghijklmnopqrstuvwxyz',
          '_0123456789'
         )
  "/>

 <xsl:variable name="vReps" select=
  "'_____________________________________'"/>
 <xsl:key name="kColNameByCode"
       match="report_header/*/text()"
       use="name(..)"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="report_row/*">
  <xsl:variable name="vNameText" select=
   "key('kColNameByCode', name())"/>

  <xsl:variable name="vElName" select=
  "translate($vNameText,
             translate($vNameText,$vAlphanum,''),
             $vReps)
  "/>
  <xsl:element name="{$vElName}">
    <xsl:value-of select="."/>
  </xsl:element>
 </xsl:template>
 <xsl:template match="report_header"/>
</xsl:stylesheet>

применительно к предоставленному XML-документу :

<report>
    <report_header>
        <c1>desc</c1>
        <c2>pr name</c2>
        <c3>pr num</c3>
        <c4>cdate</c4>
        <c5>phase</c5>
        <c6>stype</c6>
        <c7>status</c7>
        <c8>parent</c8>
        <c9>location</c9>
    </report_header>
    <report_row>
        <c1></c1>
        <c2>IT Project Message Validation</c2>
        <c3>IT-0000021</c3>
        <c4>12/14/2010 09:56 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
    <report_row>
        <c1></c1>
        <c2>David, Michael John Morning QA Test</c2>
        <c3>IT-0000020</c3>
        <c4>12/14/2010 08:12 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
</report>

создает искомый, правильный результат :

<report>
   <report_row>
      <desc/>
      <pr_name>IT Project Message Validation</pr_name>
      <pr_num>IT-0000021</pr_num>
      <cdate>12/14/2010 09:56 AM</cdate>
      <phase>Preparation</phase>
      <stype>IT Projects</stype>
      <status>Active</status>
      <parent>IT</parent>
      <location>/IT/BIOMED</location>
   </report_row>
   <report_row>
      <desc/>
      <pr_name>David, Michael John Morning QA Test</pr_name>
      <pr_num>IT-0000020</pr_num>
      <cdate>12/14/2010 08:12 AM</cdate>
      <phase>Preparation</phase>
      <stype>IT Projects</stype>
      <status>Active</status>
      <parent>IT</parent>
      <location>/IT/BIOMED</location>
   </report_row>
</report>

Примечание :

  1. Преобразование успешно преобразует любой текст с любым числомразличные не алфавитно-цифровые символы для синтаксически правильного имени XML.

  2. Эффективность достигается при использовании ключей.

2 голосов
/ 31 декабря 2010

Я знаю, что на этот вопрос уже дан ответ, но я полагаю, что я бы включил версию StAX в ColdFusion, поскольку вопрос изначально был помечен как таковой.Будет служить потомкам, если его укусят ошибки OoM с использованием XSLT:

<!--- see: http://today.java.net/pub/a/today/2006/07/20/introduction-to-stax.html --->
<cfset XMLOutputFactory = createObject("java", "javax.xml.stream.XMLOutputFactory").newInstance()>
<cfset fos = createObject("java", "java.io.FileOutputStream").init("#getDirectoryFromPath(getCurrentTemplatePath())#/destination.xml")>
<cfset bos = createObject("java", "java.io.BufferedOutputStream").init(fos)>
<cfset writer = XMLOutputFactory.createXMLStreamWriter(bos)>

<cfset fis = createObject("java", "java.io.FileInputStream").init("#getDirectoryFromPath(getCurrentTemplatePath())#/source.xml")>
<cfset bis = createObject("java", "java.io.BufferedInputStream").init(fis)>
<cfset XMLInputFactory = createObject("java", "javax.xml.stream.XMLInputFactory").newInstance()>
<cfset reader = XMLInputFactory.createXMLStreamReader(bis)>

<cfset headers = {}>
<cfset isHeaderRow = true>

<cfloop condition="#reader.hasNext()#">
    <cfset event = reader.next()>
    <cfif event EQ reader.START_ELEMENT>
        <cfswitch expression="#reader.getLocalName()#">
            <cfcase value="report">
                <cfset isHeaderRow = false>
                <cfset writer.writeStartElement(reader.getLocalName())>
            </cfcase>
            <cfcase value="report_header">
                <cfset isHeaderRow = true>
            </cfcase>
            <cfcase value="report_row">
                <cfset writer.writeStartElement(reader.getLocalName())>
                <cfset isHeaderRow = false>
            </cfcase>
            <!--- cX node --->
            <cfdefaultcase>
                <cfif isHeaderRow>
                    <!--- alphanumerics and underscores only --->
                    <cfset headers[reader.getLocalName()] = rereplacenocase(reader.getElementText(), "[^A-Z0-9\_]*", "", "all")>
                <cfelse>
                    <!--- getElementText --->
                    <cfset writer.writeStartElement(headers[reader.getLocalName()])>
                    <cfset writer.writeCharacters(reader.getElementText())>
                    <cfset writer.writeEndElement()>
                </cfif>
            </cfdefaultcase>
        </cfswitch>
    <cfelseif event EQ reader.END_ELEMENT>
        <cfif isHeaderRow><cfcontinue/></cfif>
        <cfset writer.writeEndElement()>
    </cfif>
</cfloop>

<cfset reader.close()>

<cfset writer.flush()>
<cfset writer.close()>

<!--- don't do this w/a large file b/c you'll get an OOM error --->
<cffile action="read" file="#getDirectoryFromPath(getCurrentTemplatePath())#/destination.xml" variable="transformed">
<cfdump var="#transformed#">
1 голос
/ 31 декабря 2010

Рассмотрим следующую таблицу стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="getHeaderText" match="report_header/*" use="local-name()"/>

<xsl:template match="node() | @*">
    <xsl:copy>
        <xsl:apply-templates select="node() | @*"/>        
    </xsl:copy>
</xsl:template>

<xsl:template match="report_row/*">
    <xsl:element name="{ translate( key('getHeaderText', local-name()), ' ', '_') }">        
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="report_header"/>

</xsl:stylesheet>

Применяется к XML с пробелами в заголовках:

<report>
    <report_header>
        <c1>desc</c1>
        <c2>pr name</c2>
        <c3>pr num</c3>
        <c4>cdate</c4>
        <c5>phase</c5>
        <c6>stype</c6>
        <c7>status</c7>
        <c8>parent</c8>
        <c9>location</c9>
    </report_header>
    <report_row>
        <c1></c1>
        <c2>IT Project Message Validation</c2>
        <c3>IT-0000021</c3>
        <c4>12/14/2010 09:56 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
    <report_row>
        <c1></c1>
        <c2>David, Michael John Morning QA Test</c2>
        <c3>IT-0000020</c3>
        <c4>12/14/2010 08:12 AM</c4>
        <c5>Preparation</c5>
        <c6>IT Projects</c6>
        <c7>Active</c7>
        <c8>IT</c8>
        <c9>/IT/BIOMED</c9>
    </report_row>
</report>

Это дает такой результат:

<report>
    <report_row>
       <desc/>
       <pr_name>IT Project Message Validation</pr_name>
       <pr_num>IT-0000021</pr_num>
       <cdate>12/14/2010 09:56 AM</cdate>
       <phase>Preparation</phase>
       <stype>IT Projects</stype>
       <status>Active</status>
       <parent>IT</parent>
       <location>/IT/BIOMED</location>
    </report_row>
    <report_row>
       <desc/>
       <pr_name>David, Michael John Morning QA Test</pr_name>
       <pr_num>IT-0000020</pr_num>
       <cdate>12/14/2010 08:12 AM</cdate>
       <phase>Preparation</phase>
       <stype>IT Projects</stype>
       <status>Active</status>
       <parent>IT</parent>
       <location>/IT/BIOMED</location>
    </report_row>
</report>
1 голос
/ 31 декабря 2010

Вы можете использовать функцию перевода, чтобы удалить нежелательные символы из имени.

Вы можете использовать обычные шаблоны с подстановочными знаками, чтобы соответствовать любому дочернему элементу; используйте режим, чтобы предотвратить это, чтобы мешать обычному шаблону подстановочного знака. И вы можете использовать функцию local-name для поиска элемента по имени элемента.

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:exslt="http://exslt.org/common">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
    <report>
        <xsl:apply-templates select="/report/report_row"/>          
    </report>
</xsl:template>

<xsl:template match="/report/report_row">
<report_row>
    <xsl:apply-templates mode="wildcard"/>
</report_row>
</xsl:template> 

<xsl:template match="*" mode="wildcard">
    <xsl:variable name="elemname" select="local-name()"/>
    <xsl:variable name="elemcontent" select="/report/report_header/*[local-name()=$elemname]"/>
    <xsl:element name='{translate($elemcontent," ()","")}'><xsl:value-of select="current()"/></xsl:element>
</xsl:template>

</xsl:transform>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...