예를 들어 라벨의 필드를 해당 라벨 내의 라벨로 변환하고 싶습니다.
<book name="Data Structure" price="250" pages="350"/>
도착하다
<book name="Data Structure">
<price>250</price>
<pages>350</pages>
</book>
xmlstarlet
Linux 명령줄에서 또는 를 사용하여 이 작업을 수행 하고 싶습니다 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 xmlstarlet
or 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
그런 다음 ( yq
YAML 파서 래퍼의 일부 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