【问题标题】:How to use Perl to add a new child node in XML in the start of the node value如何使用 Perl 在 XML 中在节点值的开头添加一个新的子节点
【发布时间】:2015-10-17 21:03:54
【问题描述】:

我正在尝试使用 Perl 中的 XML::LibXML 将子元素添加到 XML 中节点值开头的节点。我的 XML 是:

<root>
<book>
<title>
Test title for xml
</title>
<para n=1> para1 information </para>
<para n=2><head value="PARA HEADING"/>para2 information<subpara i=1>subpara Info</subpara><xyz/></para>
</book>
</root>

我想要的输出是:

<root>
<book>
<title>
Test title for xml
</title>
<para n=1><head value="PARA HEADING"/>para1 information </para>
<para n=2><head value="PARA HEADING"/>para2 information<subpara i=1><head value="PARA HEADING"/>Subpara Info</subpara><xyz/></para>
</book>
</root>

如果“para”或“subpara”中不存在子节点“head”,我想添加一个如上所述的子节点。

我试过这段代码:

#!/usr/local/bin/perl5.8.8

use XML::LibXML;

my $xml_parser = XML::LibXML->new();
my $xml_doc    = $xml_parser->parse_file( xml_file . xml );
my $root       = $xml_doc->getDocumentElement();
my $xml_xc     = XML::LibXML::XPathContext->new( $root );

my @array_list = ( para, deck );

foreach my $xml_sections ( $xml_xc->findnodes( '//*' ) ) {

    if ( $xml_sections->nodeName ne "head" ) {

        my $marker_flag = 0;

        foreach my $first_child ( $xml_sections->childNodes() ) {

            if ( $first_child->nodeName eq "head" ) {
                $marker_flag = 1;
                last;
            }
        }

        if ( !$marker_flag ) {

            foreach my $array_elt ( @array_list ) {

                if ( $array_elt eq $xml_sections->nodeName ) {
                    my $new_tag = $xml_doc->createElement( "head" );
                    my $value   = "PARA HEADING";
                    my $att1    = $xml_doc->createAttribute( "value", "$value" );
                    $new_tag->setAttributeNode( $att1 );
                    $xml_sections->addChild( $new_tag );
                }
            }
        }
    }
}

print $root->toString();

exit 0;

我的输出是:

<root>
<book>
<title>
Test title for xml
</title>
<para n=1>para1 information <head value="PARA HEADING"/></para>
<para n=2><head value="PARA HEADING"/>para2 information<subpara i=1>subpara Info<head value="PARA HEADING"/></subpara><xyz/></para>
</book>
</root>

我怎样才能做到这一点?

【问题讨论】:

  • 您没有将文本元素添加到您正在添加的 $new_tag 中,并且您没有删除用于 para1 信息的文本元素。所以 仍然有文本,而 没有。

标签: xml perl


【解决方案1】:

你让自己的事情变得非常困难!例如,没有必要涉及XML::LibXML::XPathContext,除非您的 XML 数据具有非默认名称空间,而您的示例没有。此外,该 parasubpara 元素(如 n=1)的属性值应该在它们周围加上引号,给出 n="1" 等等

这是一个解决方案,它使用 XPath 表达式查找所有 parasubpara 元素,并使用 exists 检查每个元素是否已经有 head 子元素。标量 $head 设置有您要插入的数据,并在找到的每个元素的第一个子元素之前插入它的克隆

use strict;
use warnings;

use XML::LibXML;

my $parser = XML::LibXML->new;

my $doc = $parser->parse_fh(*DATA);

my $head = $parser->parse_balanced_chunk('<head value="PARA HEADING"/>');

for my $para ( $doc->findnodes('//para | //subpara') ) {
    if ( not $para->exists('head') ) {
        $para->insertBefore($head->cloneNode(1), $para->firstChild);
    }
}

print $doc;



__DATA__
<root>
<book>
<title>
Test title for xml
</title>
<para n="1"> para1 information </para>
<para n="2"><head value="PARA HEADING"/>para2 information<subpara i="1">subpara Info</subpara><xyz/></para>
</book>
</root>

输出

<?xml version="1.0"?>
<root>
<book>
<title>
Test title for xml
</title>
<para n="1"><head value="PARA HEADING"/> para1 information </para>
<para n="2"><head value="PARA HEADING"/>para2 information<subpara i="1"><head value="PARA HEADING"/>subpara Info</subpara><xyz/></para>
</book>
</root>

【讨论】:

    【解决方案2】:

    您正在寻找XML::LibXML::NodeinsertBeforeaddChild 方法

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    use feature qw(say);
    
    use XML::LibXML;
    
    my $dom = XML::LibXML->load_xml( IO => \*DATA );
    
    for my $node ( $dom->findnodes("//para | //subpara") ) {
        my $newnode = XML::LibXML->load_xml( string => '<head value="PARA HEADING"/>' )->findnodes('//*')->[0];
    
        my @children = $node->childNodes();
    
        if ( !@children ) {
            $node->addChild($newnode);
        } elsif ( $children[0]->nodeName ne 'head' ) {
            $node->insertBefore( $newnode, $children[0] );
        }
    }
    
    print $dom->toString;
    
    __DATA__
    <root>
    <book>
    <title>
    Test title for xml
    </title>
    <para n="1"> para1 information </para>
    <para n="2"><head value="PARA HEADING"/>para2 information<subpara i="1">subpara Info</subpara><xyz/></para>
    </book>
    </root>
    

    输出:

    <?xml version="1.0"?>
    <root>
    <book>
    <title>
    Test title for xml
    </title>
    <para n="1"><head value="PARA HEADING"/> para1 information </para>
    <para n="2"><head value="PARA HEADING"/>para2 information<subpara i="1"><head value="PARA HEADING"/>subpara Info</subpara><xyz/></para>
    </book>
    </root>
    

    【讨论】:

    • 不需要if;即使@children 为空,“else”子句也有效。
    • 确实如此,尽管条件需要以 !@children || 为前缀。
    • 对,错过了这是一个elsif。所以我的意思是你可以使用if ( !@children || $children[0]-&gt;nodeName ne 'head' ) { $node-&gt;insertBefore( $newnode, $children[0] ); }
    【解决方案3】:

    此外,您可以使用其&lt;xsl:when&gt;&lt;xsl:otherwise&gt; 逻辑运行XSLT 转换。作为信息,XSLT 是声明性的专用编程语言(与 SQL 的类型相同,但具有数据库),专门用于转换、样式化、重新格式化或重新构造 XML 文档。

    Perl (generic script)

    use XML::LibXML;
    use XML::LibXSLT;
    
    my $xml_parser  = XML::LibXML->new();
    my $xml_doc     = $xml_parser->parse_file($XML_FILENAME);
    
    my $xslt_parser = XML::LibXSLT->new;    
    my $xsl_doc     = $xml_parser->parse_file($XSL_FILENAME);
    
    my $stylesheet  = $xslt_parser->parse_stylesheet($xsl_doc);
    my $results     = $stylesheet->transform($xml_doc);
    my $output      = $stylesheet->output_string($results);
    
    print $stylesheet->output_string($results);
    

    XSLT另存为 .xsl 文件以供上面使用

    <?xml version="1.0" ?> 
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">  
      <xsl:output method="xml" indent="yes"/>
    
        <xsl:template match="@*|node()">
          <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
          </xsl:copy>
        </xsl:template>
    
        <xsl:template match="para" name="paratemplate">      
          <xsl:choose>        
            <xsl:when test="string-length(head/@value)>0">
              <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
              </xsl:copy> 
            </xsl:when>
            <xsl:otherwise>
              <para>
                <xsl:apply-templates select="@*"/>
                <xsl:element name="head">
                  <xsl:attribute name="value">PARA HEADING</xsl:attribute>
                </xsl:element>
                <xsl:apply-templates select="text()"/>
              </para>          
            </xsl:otherwise>
          </xsl:choose>      
        </xsl:template>
    
      <xsl:template match="subpara" name="subparatemplate">      
          <xsl:choose>        
            <xsl:when test="string-length(head/@value)>0">
              <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
              </xsl:copy> 
            </xsl:when>
            <xsl:otherwise>
              <subpara>
                <xsl:apply-templates select="@*"/>  
                <xsl:element name="head">            
                  <xsl:attribute name="value">PARA HEADING</xsl:attribute>              
                </xsl:element>
                <xsl:apply-templates select="text()"/>  
              </subpara>          
            </xsl:otherwise>
          </xsl:choose>      
        </xsl:template>
    </xsl:stylesheet>
    

    输出

    <?xml version="1.0"?>
    <root>
      <book>
        <title>
    Test title for xml
    </title>
        <para n="1"><head value="PARA HEADING"/> para1 information </para>
        <para n="2"><head value="PARA HEADING"/>para2 information<subpara i="1"><head value="PARA HEADING"/>subpara Info</subpara><xyz/></para>
      </book>
    </root>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多