如何使用XML::LibXML解析Apple PLIST格式的XML字典结构
问题原因
- key无输出:你的代码尝试读取
<dict>节点的key属性,但Apple PLIST格式里key是独立的子节点,不是父节点<dict>的属性,所以findvalue('@key')永远返回空值。 - value拼接输出:
textContent方法会返回当前节点及其所有后代节点的全部文本内容拼接结果,你直接对<dict>节点调用该方法,自然会把该字典下所有key、value的文本全部拼在一起输出。
PLIST的字典结构逻辑是固定的:每个<dict>节点下的子节点严格按[key节点, value节点]成对出现,value节点可以是string、integer、array、dict、布尔等类型。
修复方案
方案1:基于XML::LibXML手动适配PLIST结构
适合不想额外安装其他模块的场景,递归实现适配PLIST的所有嵌套结构:
#!/usr/bin/perl use strict; use warnings; use XML::LibXML; my $filename = '/Users/user/path/to/file'; # 加载XML文件 my $xml = XML::LibXML->load_xml(location => $filename); # 递归解析PLIST节点的通用函数 sub parse_plist_node { my ($node) = @_; my $node_type = $node->nodeName; # 解析字典类型 if ($node_type eq 'dict') { my %dict; # 跳过空白节点,只取有效子节点 my @children = $node->nonBlankChildNodes(); # 按key/value成对遍历 for (my $i = 0; $i < @children; $i += 2) { my $key_node = $children[$i]; next unless $key_node->nodeName eq 'key'; my $key = $key_node->textContent; my $value_node = $children[$i + 1]; $dict{$key} = parse_plist_node($value_node); } return \%dict; } # 解析数组类型 elsif ($node_type eq 'array') { my @array; for my $child ($node->nonBlankChildNodes()) { push @array, parse_plist_node($child); } return \@array; } # 解析布尔类型 elsif ($node_type eq 'true' || $node_type eq 'false') { return $node_type eq 'true' ? 1 : 0; } # 解析string、integer等基础类型,直接返回文本值 else { return $node->textContent; } } # 解析PLIST根字典 my $root_dict = $xml->findnodes('//plist/dict')->shift; my $plist_data = parse_plist_node($root_dict); # 测试输出:打印所有快捷短语的缩写和使用次数 for my $clipping (@{$plist_data->{Clippings}}) { print "快捷缩写:$clipping->{Abbreviation},总使用次数:$clipping->{TotUsed}\n"; }
方案2:使用专门的PLIST解析模块
直接用CPAN的Mac::PropertyList模块,不需要手动处理XML结构,解析更稳定:
#!/usr/bin/perl use strict; use warnings; use Mac::PropertyList qw(parse_plist_file); my $filename = '/Users/user/path/to/file'; # 直接解析PLIST文件为Perl结构 my $plist_data = parse_plist_file($filename); # 测试输出,调用value方法取实际值 for my $clipping (@{$plist_data->{Clippings}->value}) { print "快捷缩写:".$clipping->{Abbreviation}->value.",总使用次数:".$clipping->{TotUsed}->value."\n"; }
内容的提问来源于stack exchange,提问作者Polo
相关产品推荐
相关产品推荐

