2013-02-26 1 views
-1

Я работаю с xml, который мне нужно скопировать и обновить, чтобы передать его для дальнейшей обработки. Проблема, с которой я сталкиваюсь, заключается в том, что я не нашел эффективного метода для этого. По сути, я хочу обновить некоторые данные, условно, а затем скопировать все узлы, которые не были обновлены. Почему это сложно, связано с объемом и дисперсией числа и имени узлов, которые нужно скопировать. Я также хочу НЕ копировать узлы, которые не имеют текстового значения. Ниже приведен пример:выборочно копировать и обновлять узлы xml с помощью XSLT

ВХОД XML-

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>John</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>OR</HomeState> 
     ... 
     <nodeN>text</nodeN> 
    </PersonProfile> 
</root> 

Узел «PersonProfile» является лишь одним из нескольких наборов узлов внутри элемента «корень», каждый со своим собственным подмножеством данных. Такие, как почтовый адрес, контактная информация о чрезвычайных ситуациях и т. Д. То, что я пытаюсь сделать, это обновить узлы, если переменная имеет новое значение для них, а затем скопировать все узлы, которые не были обновлены.

Вот мой текущий XSLT

<xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 

<xsl:variable name='updateData' select='document("report")'/> 

<!-- Identity Transform --> 
<xsl:template match='@* | node()'> 
    <xsl:if test'. != ""'> 
     <xsl:copy> 
      <xsl:apply-templates select='@* | node()'/> 
     </xsl:copy> 
     </xsl:if> 
    </xsl:template> 

<!-- Template to update Person Profile --> 
    <xsl:template match='PersonProfile'> 
    <xsl:copy> 
     <xsl:apply-templates select='*'/>  
     <xsl:element name='Name'> 
      <xsl:if test='exists($updateData/Preferred)'> 
       <xsl:element name='FirstName'> 
        <xsl:value-of select='$reportData/FirstName'/> 
       </xsl:element> 
      </xsl:if>    
      <xsl:if test='exists($updateData/Preferred)'> 
       <xsl:element name='PreferredName'> 
        <xsl:value-of select='$updateData/Preferred'/> 
       </xsl:element> 
      </xsl:if> 
      <xsl:if test='exists($updateData/Middle)'> 
      <xsl:element name='MiddleName'> 
       <xsl:value-of select='$updateData/Middle'/> 
      </xsl:element> 
      </xsl:if> 
      <xsl:if test='exists($updateData/LastName)'> 
       <xsl:element name='LastName'> 
        <xsl:value-of select='$updateData/wd:LastName'/> 
       </xsl:element> 
      </xsl:if> 
     </xsl:element> 
     <xsl:if test='exists($updateData/Country)'> 
      <xsl:element name='Country'> 
       <xsl:value-of select='$updateData/Country'/> 
      </xsl:element> 
     </xsl:if> 
     .... 
     <!-- follows same structure until end of template --> 
    </xsl:copy> 
    </xsl:template> 

<!-- More Templates to Update other Node sets --> 

</xsl:stylesheet> 

Что сейчас происходит, является то, что это копирование всех узлов, а затем добавить значения обновлений. Использование Saxon-PE 9.3.0.5, я получаю результат, похожий на этот:

Пример вывода

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>John</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>OR</HomeState> 
     ... 
     <nodeN>text</nodeN> 
     <PreferredName>Jonathan</PreferredName> 
     <HomeState>WA</HomeState> 
    </PersonProfile> 
</root> 

Я понимаю, что это происходит потому, что я подаю шаблоны ко всем узлам в PersonProfile и что я могу указать, какие узлы исключить, но я чувствую, что это очень плохое решение, поскольку объем узлов может превышать 30 или более, и для этого потребуется письменное значение для каждого из них. Я верю, что XML имеет более элегантное решение, чем явное перечисление каждого из этих узлов. Я хотел бы иметь такой:

Желаемая Выход

<root> 
    <PersonProfile xmlns:'namespace'> 
     <ID>0001</ID> 
     <Name> 
      <FirstName>Jonathan</FirstName> 
      <PreferredName>Jonathan</PreferredName> 
      <MiddleName>A</MiddleName> 
      <LastName>Doe</LastName> 
     </Name> 
     <Country>US</Country> 
     <Biirthdate>01-01-1980</Birthdate> 
     <BirthPlace> 
      <City>Townsville</City> 
      <State>OR</State> 
      <Country>US</Country> 
     </Birthplace> 
     <Gender>Male</Gender> 
     <HomeState>WA</HomeState> 
     ... 
     <nodeN>text</nodeN> 
    </PersonProfile> 
</root> 

Если кто-то может помочь мне создать структуру шаблона, который будет работать для структуры XML, я бы очень признателен. Он должен быть «повторно использован», поскольку существуют аналогичные структуры узлов, такие как профиль лица, к которому я должен был бы применить его, но с разными именами узлов и количеством элементов и т. Д.

Заранее за любую помощь!

  • J
+0

Итак, я нашел потенциальное решение, по сути, я создаю шаблон для каждого xpath, который можно обновить, а затем применить, - который работает достаточно хорошо. У меня только 1 проблема - как использовать шаблон, когда путь не существует! Допустим, что человеку не хватает Home State - я не могу это сделать, так как я могу сказать шаблону о создании нового узла? Спасибо! –

ответ

1

Это должно работать для Вашего оригинального вопроса:

<xsl:stylesheet version="2.0" 
       xmlns:xs="http://www.w3.org/2001/XMLSchema" 
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 

    <xsl:variable name='updateData' select='document("report")'/> 

    <!-- Identity Transform --> 
    <xsl:template match='@* | node()' name='copy'> 
     <xsl:copy> 
     <xsl:apply-templates select='@* | node()'/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match='*[not(*)]'> 
    <xsl:variable name='matchingValue' 
        select='$updateData/*[name() = name(current())]'/> 
    <xsl:choose> 
     <xsl:when test='$matchingValue'> 
     <xsl:copy> 
      <xsl:apply-templates select='@*' /> 
      <xsl:value-of select='$matchingValue'/> 
     </xsl:copy> 
     </xsl:when> 
     <xsl:when test='normalize-space()'> 
     <xsl:call-template name='copy' /> 
     </xsl:when> 
    </xsl:choose> 
    </xsl:template> 
</xsl:stylesheet> 

Что касается вставки новых элементов, которые не присутствуют в исходном XML, это сложнее. Не могли бы вы открыть для этого отдельный вопрос? У меня могут быть некоторые идеи о том, как подойти к этому.

+0

Спасибо! Я проверю это в ближайшее время.Что касается моего второго вопроса, я создал этот пост. http://stackoverflow.com/questions/15081758/insert-xpath-during-copy-using-apply-templates Еще раз спасибо за любую помощь, которую вы можете предложить! –

+0

Хотя это не сработает для меня, я все же принял ответ, потому что это правильно в предположении 1 - что переменная xpath в переменной updateData будет соответствовать имени элемента в исходном xml. Я не указал, что это не всегда так. Я думаю, что я могу изменить это для работы, но просто для того, чтобы уточнить, что исходное имя xpath/element не соответствует xpath для данных в переменной updateData. Еще раз спасибо за ответ! –