2012-06-18 6 views
4

Я пытаюсь найти лучший (эффективный) способ сделать это.XSLT - Удалить узлы + атрибуты, соответствующие Xpath

У меня есть документ XML среднего размера. В зависимости от конкретных настроек некоторые части должны быть отфильтрованы по соображениям безопасности.

Я буду делать это в XSLT, поскольку он настраивается, и никакой код не нуждается в изменении.

Я огляделся вокруг, но не получил большой удачи.

Например:

У меня есть следующие XPath:

//*[@root='2.16.840.1.113883.3.51.1.1.6.1'] 

Whicrooth дает мне все узлы с корнем атрибута равным конкретному OID. В этих узлах я хочу иметь все атрибуты нескольких кроме (напр. Foo и бар) стерта, а затем с другой атрибут добавлен (напр. причина)

мне нужно иметь несколько XPath выражения, которые могут быть запущены до нуля на определенном узле и очистить его содержимое аналогичным образом в отношении узлов со специфическими атрибутами.

Я играл с информацией из:

XPath expression to select all XML child nodes except a specific list?

и Remove Elements and/or Attributes by Name per XSL Parameters

Обновит вскоре, когда я могу получить доступ, что то, что я "ве сделано до сих пор

Пример. :

XML до трансформации ОБНОВЛЕНИЕ: Я хочу, чтобы отфильтровать расширение, а затем все значения в документе, которые соответствуют значение этого атрибута расширения:

<root> 
    <childNode> 
     <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" extension="123" type="innerChildness"/> 
     <innerChildSibling/> 
    </childNode> 
    <animals> 
    <cat> 
     <name>123</name> 
    </cat> 
    </animals> 
    <tree/> 
    <water root="2.16.840.1.113883.3.51.1.1.6.1" extension="1223" type="liquidLIke"/> 
</root> 

После

<root> 
    <childNode> 
     <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK"/> <!-- filtered --> 
     <innerChildSibling/> 
    </childNode> 
    <animals> 
     <cat> 
     <name>****</name> 
     </cat> <!-- cat was filtered --> 
    </animals> 
    <tree/> 
    <water root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK"/> <!-- filtered --> 
</root> 

Я могу использовать XSLT2.

Я пытаюсь это без везения (для стартеров)

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

    <xsl:param name="OIDAttrToDelete" select="'extension'"/> 

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

    <!-- Get all nodes for the OID --> 
    <xsl:template match="//*[@root='2.16.840.1.113883.3.51.1.1.6.1']"> 
     <xsl:if test="name() = $OIDAttrToDelete"> 
      <xsl:attribute name="nullFlavor">MSK</xsl:attribute> 
      <xsl:call-template name="identity"/>    
     </xsl:if> 
    </xsl:template>  
</xsl:stylesheet> 
+0

В конечном итоге то, что я надеюсь, это возможность добавлять (как только я узнаю больше ... я не эксперт по XSLT) дополнительные фильтры для фильтрации XML. Это легче поддерживать, чем обновлять код каждый раз. SO каждый раз может иметь разные правила. –

+0

Ryan Ternier: Это не слишком сложно реализовать все эти требования в преобразовании XSLT 2.0. –

ответ

2
<xsl:param name="OIDAttrToDelete" select="'extension'" /> 

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

<xsl:template match="@*"> 
    <xsl:choose> 
    <xsl:when test="../@root = '2.16.840.1.113883.3.51.1.1.6.1'"> 
     <xsl:copy-of select=".[not(contains($OIDAttrToDelete, name()))]" /> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:copy-of select="."> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 

Примечания:

Я создал шаблон, который соответствует только атрибуты и решает, следует ли копировать их или нет. Таким образом, мне не нужно вмешиваться в шаблон идентификации.

Нет необходимости указывать имя шаблона идентификации. Просто позвоните <apply-templates> с соответствующим выражением выбора, и процессор вызовет его автоматически.

Соответствующие выражения в шаблонах не являются полными выражениями XPath. Вам не нужно соответствовать //*[predicate].Использовать *[predicate] достаточно.

Если вы беспокоитесь о безопасности, я бы рассмотрел вместо этого белый список ($OIDAttrToKeep).

Если $OIDAttrToDelete список значений (например, разделенная запятая), вы должны включать в себя сепаратор в тесте:

.[ 
    not(
    contains(
     concat(',', $OIDAttrToDelete, ','), 
     concat(',', name(), ',') 
    ) 
) 
] 

, чтобы избежать частичного совпадения имени.

Если ваш родитель OID должен быть настраиваемым, вы можете использовать ту же технику:

<xsl:template match="@*"> 
    <xsl:choose> 
    <xsl:when test=" 
     contains(
     concat(',', $OIDToStrip, ','), 
     concat(',', ../@root, ',') 
    ) 
    "> 
    <!-- ... --> 
    </xsl:when> 
    </xsl:choose> 
</xsl:template> 
+0

Эй, Томалак, Да, я буду белым, перечисляя атрибуты, которые нужно сохранить. Благодарю. собираюсь проверить это сейчас –

+0

@RyanTernier FYI В моем коде, который я исправил, произошла ошибка. – Tomalak

+0

Да, я поймал, что вчера - спасибо за обновление –

2

Вот полный XSLT 2.0 преобразование, которое, в соответствии с внешним параметром, идентифицирует элементы, имеющие определенное имя атрибута и значение и для каждых таких элементов удаляет все атрибуты, которые не являются белым списком, и добавляет другие указанные атрибуты:

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

<xsl:param name="vFilters"> 
    <filter> 
     <markerAttribute name="root">2.16.840.1.113883.3.51.1.1.6.1</markerAttribute> 
     <whiteListedAttributes> 
     <name>root</name> 
     <name>foo</name> 
     </whiteListedAttributes> 
     <addAtributes flavor="MSK" reason="Demo"/> 
    </filter> 
</xsl:param> 

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

<xsl:template match= 
"*[for $cur in ., 
     $m in $vFilters/filter/markerAttribute 
    return 
     $cur/@*[name() eq $m/@name and . eq $m] 
    ]"> 
    <xsl:copy> 
    <xsl:apply-templates select="@*"/> 
    <xsl:copy-of select= 
    "for $m 
      in $vFilters/filter/markerAttribute 
     return 
     if(current()/@* 
         [name() eq $m/@name 
        and 
         . eq $m 
         ]) 
      then 
      $m/../addAtributes/@* 
      else() 
    "/> 
    <xsl:apply-templates/> 
    </xsl:copy> 
</xsl:template> 

    <xsl:template match= 
"@*[for $cur in ., 
     $p in .., 
     $m in $vFilters/filter/markerAttribute 
    return 
      $p/@*[name() eq $m/@name and . eq $m] 
     and 
      not(name($cur) = $m/../whiteListedAttributes/name) 
    ] 
    "/> 
</xsl:stylesheet> 

Когда это преобразование является pplied на следующем документе XML (на основании представленного, но добавил один белый список атрибутов):

<root> 
    <childNode> 
     <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" 
      a="b" b="c" foo="bar" type="innerChildness"/> 
     <innerChildSibling/> 
    </childNode> 
    <animals> 
     <cat> 
      <name>bob</name> 
     </cat> 
    </animals> 
    <tree/> 
    <water root="2.16.840.1.113883.3.51.1.1.6.1" 
    z="zed" l="ell" type="liquidLIke"/> 
</root> 

разыскиваемых, правильный результат, полученные - на определенных элементах все не в белом списке атрибуты будут удалены, а две новые атрибуты, указанные в фильтре добавляются:

<root> 
     <childNode> 
      <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" foo="bar" flavor="MSK" reason="Demo"/> 
      <innerChildSibling/> 
     </childNode> 
     <animals> 
      <cat> 
        <name>bob</name> 
      </cat> 
     </animals> 
     <tree/> 
     <water root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK" reason="Demo"/> 
</root> 

Объяснение:

внешний пункт Измеритель $vFilters может содержать один или несколько фильтров, как следующее:

<filter> 
    <markerAttribute name="root">2.16.840.1.113883.3.51.1.1.6.1</markerAttribute> 
    <whiteListedAttributes> 
    <name>root</name> 
    <name>foo</name> 
    </whiteListedAttributes> 
    <addAtributes flavor="MSK" reason="Demo"/> 
</filter> 

markerAttribute элемента определяет имя и значение атрибута, идентифицирующий. В этом случае фильтр идентифицирует (для) элементы, которые имеют атрибут root, значение которого равно "2.16.840.1.113883.3.51.1.1.6.1".

В этом фильтре указаны два атрибута атрибута whitelisted: root и foo.

Два новых атрибута с указанными значениями должны добавляться к каждому идентифицированному этим фильтрующим элементом: flavor="MSK" и reason="Demo".

Внешний параметр $vFilters может содержать много фильтров, каждый из идентификации другого «типа» элемента и задание другого набор белых перечисленных имен атрибутов и новых атрибутов, которые будут добавлен.

+0

И я понятия не имел, что XSLT может это сделать! Благодарю. Для анализа документа требуется 4 мс. –

+0

@RyanTernier: Добро пожаловать. Есть ли у вас какие-либо проблемы при использовании этого решения? –

+0

Оба они отлично работают - спасибо человеку. Какой язык сценариев используется в XSLT? Это общий язык скриптов для xslt? –

Смежные вопросы