xmlstarlet을 사용하여 XML의 필드를 태그로 변환할 수 있나요?

xmlstarlet을 사용하여 XML의 필드를 태그로 변환할 수 있나요?

예를 들어 라벨의 필드를 해당 라벨 내의 라벨로 변환하고 싶습니다.

<book name="Data Structure" price="250" pages="350"/>

도착하다

<book name="Data Structure"> 
<price>250</price>
<pages>350</pages>
</book>

xmlstarletLinux 명령줄에서 또는 를 사용하여 이 작업을 수행 하고 싶습니다 sed.

답변1

process.xsl:

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

  <xsl:template match="//book">
    <xsl:element name="book">
      <xsl:apply-templates select="./@*"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="book/@*">
      <xsl:if test="name() = 'name'">
    <xsl:attribute name="{name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
      </xsl:if>
      <xsl:if test="name() != 'name'">
    <xsl:element name="{name()}">
      <xsl:value-of select="."/>
    </xsl:element>
      </xsl:if>
  </xsl:template>
</xsl:stylesheet>

input.xml:

<book name="Data Structure" price="250" pages="350"/>

주문하다:

xsltproc process.xsl input.xml

산출:

<?xml version="1.0"?>
<book name="Data Structure">
  <price>250</price>
  <pages>350</pages>
</book>

답변2

질문에 "use xmlstarletor sed"라는 내용이 있다는 것을 알고 있지만 이러한 도구 중 하나를 사용하려면 많은 입력이 필요합니다(그리고 다음을 사용하여 sed수정 ).어느구조화된 문서 형식은 권장되지 않습니다.) 이를 더 쉽게 만들어 주는 다른 XML 인식 도구가 있습니다.

다음과 같은 XML 문서를 가정합니다.

<root>
<book name="Data Structure 1" price="250" pages="350"/>
<book name="Data Structure 2" price="350" pages="250"/>
<book name="Data Structure 3" price="450" pages="150"/>
</root>

xq그런 다음 ( yqYAML 파서 래퍼의 일부 jq)을 사용할 수 있습니다 .https://kislyuk.github.io/yq/) 원하는 것을 표현하려면 표현을 사용하세요 jq.

xq도구는 예제 XML 문서를 동등한 JSON 문서로 구문 분석합니다.

{
  "root": {
    "book": [
      {
        "@name": "Data Structure 1",
        "@price": "250",
        "@pages": "350"
      },
      {
        "@name": "Data Structure 2",
        "@price": "350",
        "@pages": "250"
      },
      {
        "@name": "Data Structure 3",
        "@price": "450",
        "@pages": "150"
      }
    ]
  }
}

다음 표현식을 적용하여 배열을 반복하고, .root.book[]그렇지 않은 키에서 첫 번째 문자를 제거하여 각 JSON 요소의 키를 수정합니다. 이름에 약어가 있는 키는 XML의 속성에 해당하므로 이를 삭제하면 키가 노드의 속성이 아닌 XML 노드로 전환됩니다.@@name@@

xq -x '.root.book[] |= (with_entries(select(.key != "@name").key |= ltrimstr("@")))' file.xml

위의 예제 파일을 사용하면 다음이 생성됩니다.

<root>
  <book name="Data Structure 1">
    <price>250</price>
    <pages>350</pages>
  </book>
  <book name="Data Structure 2">
    <price>350</price>
    <pages>250</pages>
  </book>
  <book name="Data Structure 3">
    <price>450</price>
    <pages>150</pages>
  </book>
</root>

XML 문서가 실제로 단일 노드인 경우

<book name="Data Structure" price="250" pages="350"/>

그런 다음 사용

xq -x '.book |= (with_entries(select(.key != "@name").key|=ltrimstr("@")))' file.xml

.book이는 위와 동일한 표현식이지만 배열의 요소가 아닌 최상위 섹션에만 적용됩니다 .root.book[].

답변3

사용하지 마십시오 sed. 작업에 적합한 도구가 아닙니다.

나는 Perl을 직접 사용합니다:

#!/usr/bin/env perl
use strict;
use warnings;

use XML::Twig;

my $twig = XML::Twig->new( 'pretty_print' => 'indented_a' );
$twig->parsefile ( 'your_file.xml' );

foreach my $thing ( $twig -> root -> children ) {

    my $newthing = $twig -> root -> insert_new_elt($thing->tag);
    foreach my $key ( keys %{$thing -> atts()} ) {
        $newthing -> insert_new_elt($key, $thing -> att($key));
    }
    $thing -> delete;
}

$twig->print;

산출:

<root>
  <book>
    <pages>350</pages>
    <name>Data Structure</name>
    <price>250</price>
  </book>
</root>

(익명) 해시를 사용하고 있으므로 이는 매우 간단합니다 att(). 속성을 선택하려면 좀 더 많은 작업을 수행해야 합니다. 원하는 속성을 정의해야 합니다.유지하다 name이를 상위 요소의 속성으로 삽입합니다.

다음을 map사용하면 약간 머리가 아플 수 있습니다.

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

use XML::Twig;

my %keep_att = ( name => 1 );
my $twig = XML::Twig->new( 'pretty_print' => 'indented_a' );
$twig->parse( \*DATA );

foreach my $thing ( $twig->root->children ) {
    my $newthing = $twig->root->insert_new_elt( $thing->tag,
        { map { $_ => $thing->att($_) } keys %keep_att } );

    foreach my $key ( keys %{ $thing->atts() } ) {
        next if $keep_att{$key};
        $newthing->insert_new_elt( $key, $thing->att($key) );
    }
    $thing->delete;
}

$twig->print;

__DATA__
<root>
<book name="Data Structure" price="250" pages="350"/>
</root>

그러면 다음이 생성됩니다.

<root>
  <book name="Data Structure">
    <price>250</price>
    <pages>350</pages>
  </book>
</root>

이제 map기본적으로 유지하려는 속성을 분리하고 새 요소에 다시 삽입하는 것과 유지하려는 요소가 발생합니다.아니요그들을 지켜 아이들로 만들고 싶습니다.

이 같은:

foreach my $thing ( $twig->root->children ) {

    my %attributes = %{$thing->atts()};
    my %new_children; 
    foreach my $attr ( keys %attributes ) {
       if ( $keep_att{$attr} ) { 
           #leave it in %attributes; 
       }
       else {
           $new_children{$attr} = $attributes{$attr}; 
           delete $attributes{$attr}
       }
    }
    print Dumper \%attributes;
    print Dumper \%new_children;

    my $newthing = $twig->root->insert_new_elt( $thing->tag,
        { %attributes } );

    foreach my $key ( keys %new_children ) {
        $newthing->insert_new_elt( $key, $new_children{$key} );
    }
    $thing->delete;
}

답변4

Linux 명령줄에서 또는를 사용하여 xmlstarlet이 작업을 수행 하고 싶습니다.sed

1.6.1 및 입력 XML 파일을 사용하여 xmlstarlet다음 명령은 원하는 출력을 생성합니다.

xmlstarlet edit --omit-decl --var T 'book' \
 -s '$T' -t elem -n 'price'  -u '$prev' -x 'string(../@price)'  -d '$T/@price' \
 -s '$T' -t elem -n 'pages'  -u '$prev' -x 'string(../@pages)'  -d '$T/@pages' \
file.xml

어디

  • 이 변수에는 변환할 요소의 노드 세트가 포함되어 있습니다 T. 입력의 루트 요소에 s가 포함된 경우 book입력 파일 book(또는 *)의 단일 요소를 사용하면 먼저 모두 선택됩니다.book*/book//book(//book)[1]
  • -s/ --subnode( ) 하위 노드를 각 요소의 속성으로 포함하는 ( )라는 요소를 생성합니다.-t elem-n$T
  • -u/ 상대 XPath 표현식을 사용하여 새로 생성된 각 요소( )에 --update값을 삽입합니다 ( ).$prev-x
  • -d/ 변환 후 --delete각 요소의 속성을 제거합니다.$T

xmlstarlet edit코드는 편의 $prev(일명 ) 노드를 사용하여 $xstar:prev가장 최근의 -i/ --insert, -a/ --append또는 -s/ 옵션으로 --subnode생성된 노드를 참조할 수 있습니다. 예$prev문서/xmlstarlet.txt그리고 소스코드 예제/ed-backref*.


name예를 들어 스크립트 생성기의 경우 중복을 제거 하기 위해 모든 s( 제외)의 속성 이름을 나열하려면 다음과 같이 book말할 수 있습니다.

xmlstarlet select -t \
  -m '//book/@*[name() != "name"]' -v 'name()' -n \
file.xml | 
awk '!seen[$1]++'

xmlstarlet또는 EXSLT 지원 이외의 도구를 사용하지 마십시오.동적: 지도 기능:

xmlstarlet select -t \
  --var T='//book/@*[name() != "name"]' \
  -m 'set:distinct(dyn:map($T,"name()"))' -v . -n \
file.xml

관련 정보