【问题标题】:Perl decoding xml into hashPerl 将 xml 解码为哈希
【发布时间】:2016-07-25 08:12:05
【问题描述】:

我需要解码一个复杂的 XML 结构。 XML 如下所示:

<?xml version="1.0" encoding="ISO-8859-1"?>
    <MainNode comment="foo">
      <FirstMainBranch>
        <Struct>
          <String name="aStringValueUnderMainBranch" comment="Child node under first main branch"/>
          <String name="anotherStringValueUnderMainBranch" comment="Child node under first main branch"/>
          <Integer name="anIntegerValueUnderMainBranch" comment="Child node under first main branch"/>
          <List name="aList" comment="According to me this node should be an array, it could contain one or more child elements">
            <Struct comment="The node name means that, the child nodes are grouped, I think that the most appropriate structure here is hash. 
        The node itself doesn't have name attribute, which means that it only shows the type of the element">
          <String name="first" comment="
            Default Value: 0 
                        "/>
          <Long name="second" comment="
            Default Value: 0 

                          "/>
          <Long name="third" comment="
            Default Value: 0 

                        "/>
        </Struct>
      </List>
      <List name="secondList" comment="According to me this node should be array, it could contain one or more child elements">
        <Struct comment="The node name means that, the child nodes are grouped, I think that the most appropriate structure here is hash. 
        The node itself doesn't have name attribute, which means that it only shows the type of the element
                    ">
          <String name="first" comment="
            Default Value: 0 

                          "/>
          <Long name="second" comment="
            Default Value: 0 

                          "/>        
        </Struct>
      </List>
      <Struct name="namedStruct" comment="Here the struct element has a name, which means that it should be decoded
                    ">
        <List name="thirdList" comment="Again list, but now it is inside struct element, and it contains struct element
                ">
          <Struct comment="The node name means that, the child nodes are grouped, I think that the most appropriate structure here is hash.">
            <Integer name="first" comment="Child element of the struct"/>
          </Struct>
        </List>

      </Struct>

    </Struct>
  </FirstMainBranch>
  <SecondMainBranch>
    <Struct comment="">
      <Struct name="namedStructAgain" comment="
                ">
        <String name="First" comment="
                  "/>
        <String name="Second" comment=""/>

      </Struct>
    </Struct>
  </SecondMainBranch>
</MainNode>

我认为最合适的容器是哈希(如果您的意见不同,请告诉我)。 我发现很难解码,因为:

  1. 主节点没有“name”属性,但应该存在于最终结构中

  2. 只有当有“名称”属性时才应该读取子节点,但它们的数据类型(结构)取决于未解码的父元素。

  3. 其中一些父元素具有“名称”属性 - 在这种情况下,它们应该存在于最终结构中。

  4. 我不关心整数、长整数、日期时间等数据类型,它们将被读取为字符串。这里的主要问题是 List 和 Struct 类型

这是我应对任务的愚蠢尝试:

use XML::LibXML;
use Data::Dumper;
use strict;
use warnings;
my $parser=XML::LibXML->new();
my $file="c:\\joro\\Data.xml";
my $xmldoc=$parser->parse_file($file);

sub buildHash{
my $mainParentNode=$_[0];
my $mainHash=\%{$_[1]};
my ($waitNextNode,$isArray,$arrayNode);
$waitNextNode=0;
$isArray=0;
sub xmlStructure{
my $parentNode=$_[0];
my $href=\%{$_[1]};
my ($name, %tmp);
my $parentType=$parentNode->nodeName();
$name=$parentNode->findnodes('@name');
foreach my $currentNode($parentNode->findnodes('child::*')){
my $type=$currentNode->nodeName();
if ($type&&$type eq 'List'){
$isArray=1;
}
elsif($type&&$type ne 'List'&&$parentType ne 'List'){
$isArray=0;
$arrayNode=undef;
}
if ($type&&!$currentNode->findnodes('@name')&&$type eq 'Struct'){
$waitNextNode=1;
}
else{
$waitNextNode=0;
}
if ($type&&$type ne 'List'&&$type ne 'Struct'&&!$currentNode->findnodes('@name')){
#$href->{$currentNode->nodeName()}={};
xmlStructure($currentNode,$href->{$currentNode->nodeName()});
}
# elsif ($type&&$type eq 'List'&&$currentNode->findnodes('@name')){
# print "2\n";
# $href->{$currentNode->findnodes('@name')}=[];
# xmlStructure($currentNode,$href->{$currentNode->findnodes('@name')});
# }
elsif ($type&&$type ne 'List'&&$currentNode->findnodes('@name')&&$parentType eq 'List'){
push(@{$href->{$currentNode->findnodes('@name')}},$currentNode->findnodes('@name'));
xmlStructure($currentNode,$href->{$currentNode->findnodes('@name')});

}
# elsif ($type&&$type ne 'List'&&!$currentNode->findnodes('@name')&&$parentType eq 'List'){
# print "4\n";
# push(@{$$href->{$currentNode->findnodes('@name')}},{});
##print Dumper %{$arrayNode};
# xmlStructure($currentNode,$href->{$currentNode->findnodes('@name')});
# }
else{
xmlStructure($currentNode,$href->{$currentNode->findnodes('@name')});
}
}

}
xmlStructure($mainParentNode,$mainHash);
}
my %href;
buildHash($xmldoc->findnodes('*'),\%href);
print "Printing the real HASH\n";
print Dumper %href;

但还有很长的路要走,因为: 1. 在键和值之间有一个可能未定义的寄生虫元素。 2. 我找不到在需要的地方将数据类型从哈希更改为子数组的方法。

这是输出:

$VAR1 = 'FirstMainBranch';
$VAR2 = {
          '' => {
                  'aList' => {
                             '' => {
                                     'third' => {},
                                     'second' => {},
                                     'first' => {}
                                   }
                           },
                  'namedStruct' => {
                                   'thirdList' => {
                                                  '' => {
                                                          'first' => {}
                                                        }
                                                }
                                 },
                  'anotherStringValueUnderMainBranch' => {},
                  'secondList' => {
                                  '' => {
                                          'second' => {},
                                          'first' => {}
                                        }
                                },
                  'aStringValueUnderMainBranch' => {},
                  'anIntegerValueUnderMainBranch' => {}
                }
        };
$VAR3 = 'SecondMainBranch';
$VAR4 = {
          '' => {
                  'namedStructAgain' => {
                                        'First' => {},
                                        'Second' => {}
                                      }
                }
        };

任何帮助将不胜感激。 提前谢谢你。

编辑: 关于 Sobrique 的评论 - X Y 问题:

这是我要解析的示例字符串:

(1,2,"N/A",-1,"foo","bar",NULL,3,2016-03-18 08:12:00.000,2016-03-18 08:12:00.559,2016-03-18 08:12:00.520,0,0,NULL,"foo","123456789",{NULL,NULL,NULL,NULL,NULL,NULL,2016-04-17 11:59:59.999,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,null,NULL,NULL,NULL,NULL,3,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,T,0,NULL,NULL,NULL,"9876543210",NULL,"foo","0","bar","foo","a1820000264d979c","0,0",NULL,"foo","192.168.1.82","SOAP",NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL},{INPUT="bar"},{aStringValueUnderMainBranch="ET", aList[{"first", "second", "third"}, {"first", "second", "third"}], secondList[{"first", "second"}, {"first", "second"}],namedStruct{thirdList[{first},{first}]}},{namedStructAgain{"first", "second"}},NULL,NULL,NULL,NULL,NULL)

我应该以某种方式分离所有值,然后识别这部分:

{aStringValueUnderMainBranch="ET", aList[{"first", "second", "third"}, {"first", "second", "third"}], secondList[{"first", "second"}, {"first", "second"}],namedStruct{thirdList[{first},{first}]}}

作为 FirstMainBranch 并解析 XML 中显示的相应值。 之后我应该确定:

{namedStructAgain{"first", "second"}}

作为 SecondMainBranch 并获取相应的值。 这里还有一个主要数据分隔的问题,当逗号位于括号之间时,我不应该记住逗号。

【问题讨论】:

  • 听起来您正在尝试重新创建 XML::Simple(及其所有问题)。
  • 我不能完全理解 - 我应该使用 XML::Simple 来完成这项任务吗?
  • 不,我认为您不应该首先创建这种难以导航的结构。见Why is XML::Simple “Discouraged”?
  • 这闻起来像XY Problem - 停止;重新思考。你想达到什么目的? XML 比用 perl 数据结构表示的要复杂得多。但是你有 OO 为此,这就是 XML::LibXML 所做的。
  • 您好,您可能是对的。我已经用附加信息编辑了帖子,也许还有另一种方法来完成这项任务。我将从其他平面 XML 中获取值名称。该值将始终存在于第一个“大”字符串中,上例中 XML 中的某些节点是可选的,我应该估计值是否存在于 '{' 或 '[' 或 '=' 符号之前的文本中.

标签: xml perl xpath hash


【解决方案1】:

我会使用不同的方法。我不会将 XML 转换为散列,而是使用 XML::Rabbit 将其映射到对象。我写了一个小的article,讲述了如何通过一个完整的工作示例来使用它。

XML::Rabbit 有一系列优点:

  • 使用简单的 Moose 对象。
  • 使用 XPath 以声明方式定义要获取的对象。
  • 只解析/定义你想要的。无需从 XML 中获取所有信息。

如果您的 XML 文件足够小,可以使用 XPath 和 DOM,我发现这种方法非常干净且易于维护。

【讨论】:

  • 我将使用从 XML 收集的信息来解析 CSV 文件,您认为使用 XML::Rabbit 的方法是否合适?我现在开始阅读这篇文章了:)
  • 我阅读了这篇文章,但我认为它不能解决我的问题,因为获取节点名称不是问题。对我来说,困难的部分是在生成的哈希中获得正确的键数据结构。我想知道当前键是一个数组,包含哈希,还是包含哈希的数组,或者包含简单值的数组等。非常感谢您的回答:)
猜你喜欢
  • 2021-07-13
  • 2014-04-27
  • 2013-01-06
  • 2010-12-26
  • 2013-03-06
  • 2012-11-11
  • 2015-08-25
  • 1970-01-01
  • 2020-12-08
相关资源
最近更新 更多