Skip Menu |

This queue is for tickets about the Data-Plist CPAN distribution.

Report information
The Basics
Id: 71651
Status: new
Priority: 0/
Queue: Data-Plist

People
Owner: Nobody in particular
Requestors: lubo.rintel [...] gooddata.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: [PATCH] Add XML Reader
See attached file.
Subject: 0001-Add-XML-Reader.patch
From d0b7b62d04b1386254732e915f1684823287232c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel <lkundrak@v3.sk> Date: Thu, 13 Oct 2011 09:00:53 +0200 Subject: [PATCH] Add XML Reader --- Makefile.PL | 2 + lib/Data/Plist.pm | 5 ++- lib/Data/Plist/XMLReader.pm | 126 +++++++++++++++++++++++++++++++++++++++++++ t/xml-read.t | 79 +++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 1 deletions(-) create mode 100644 lib/Data/Plist/XMLReader.pm create mode 100644 t/xml-read.t diff --git a/Makefile.PL b/Makefile.PL index cde71bf..c5f6db1 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -14,5 +14,7 @@ requires('Scalar::Util'); requires('UNIVERSAL::isa'); requires('UNIVERSAL::require'); requires('XML::Writer'); +requires('XML::Parser'); +test_requires ('Test::Deep'); &WriteAll; diff --git a/lib/Data/Plist.pm b/lib/Data/Plist.pm index 3798587..3d8fc71 100644 --- a/lib/Data/Plist.pm +++ b/lib/Data/Plist.pm @@ -267,7 +267,8 @@ L<XML::Writer> =head1 BUGS AND LIMITATIONS -No XML reader is included at current. +UIDs don't seem to be sufficiently well supported by the XML +readers and writers. Please report any bugs or feature requests to C<bug-Data-Plist@rt.cpan.org>, or through the web interface at @@ -277,6 +278,8 @@ L<http://rt.cpan.org>. Alex Vandiver and Jacky Chang. +XML reader support added by Lubomir Rintel <lkundrak@v3.sk> + Based on plutil.pl, written by Pete Wilson <wilsonpm@gamewood.net> =head1 LICENSE diff --git a/lib/Data/Plist/XMLReader.pm b/lib/Data/Plist/XMLReader.pm new file mode 100644 index 0000000..a912fb2 --- /dev/null +++ b/lib/Data/Plist/XMLReader.pm @@ -0,0 +1,126 @@ +=head1 NAME + +Data::Plist::XMLReader - Creates Data::Plists from XML files + +=head1 SYNOPSIS + + # Create new + my $read = Data::Plist::XMLReader->new; + + # Read from a string + my $plist = $read->open_string($xmlstring); + + # Read from a XML file + $plist = $read->open_fh($filename); + +=head1 DESCRIPTION + +C<Data::Plist::XMLReader> takes data formatted as one of +Apple's XML property lists, either from a string or a +filehandle and returns it as a C<Data::Plist>. + +=cut + +package Data::Plist::XMLReader; + +use strict; +use warnings; + +use base qw/Data::Plist::Reader/; +use Data::Plist; + +use XML::Parser; +use MIME::Base64; + +=head1 METHODS + +=head2 walk $tree + +Returns data for given part of XML parse tree. + +=cut + +sub walk +{ + my $tree = shift; + + my @retval; + while ( @$tree ) { + my $tag = shift @$tree; + my $kids = shift @$tree; + + # Raw value + if ( $tag eq '0' ) { + # Skip empty + push @retval, $kids if $kids =~ /\S/; + next; + } + + my $attrs = shift @$kids; + + # Root + if ( $tag eq 'plist' ) { + warn 'Unknown version' + unless $attrs->{version} eq '1.0'; + push @retval, walk($kids); + + # Primitive values + } elsif ( $tag eq 'null' ) { + push @retval, [ null => undef ]; + } elsif ( $tag eq 'fill' ) { + push @retval, [ fill => 15 ]; + } elsif ( $tag eq 'integer' ) { + push @retval, [ integer => int walk($kids) ]; + } elsif ( $tag =~ /string|ustring|date|real/ ) { + push @retval, [ "$tag" => walk($kids) ]; + + } elsif ( $tag eq 'data' ) { + push @retval, [ data => MIME::Base64::decode_base64 (walk($kids)) ]; + + # Boolean + } elsif ( $tag eq 'true' ) { + push @retval, [ true => 1 ]; + } elsif ( $tag eq 'false' ) { + push @retval, [ false => 0 ]; + + # Compounds + } elsif ( $tag eq 'key' ) { + push @retval, walk($kids); + } elsif ( $tag eq 'dict' ) { + my %kiddies = walk($kids); + $kiddies{UID} = delete $kiddies{'CF$UID'} + if exists $kiddies{'CF$UID'}; + push @retval, [ dict => \%kiddies ]; + } elsif ( $tag eq 'array' ) { + push @retval, [ array => [ walk($kids) ] ]; + + # Unknown tag + } else { + push @retval, [ $tag => walk($kids) ]; + } + } + + return shift @retval unless wantarray; + return @retval; +} + +=head2 open_fh $filehandle + +Used for reading XML data from a filehandle +C<$filehandle> rather than a string. +Returns a C<Data::Plist> containing the top object +of the C<XML::Parser> tree after it's been passed to +L</walk>. + +=cut + +sub open_fh { + my $self = shift; + $self = $self->new() unless ref $self; + + my($fh) = @_; + my $tree = XML::Parser->new( Style => 'Tree' )->parse($fh); + return Data::Plist->new( data => walk($tree) ); +} + +1; diff --git a/t/xml-read.t b/t/xml-read.t new file mode 100644 index 0000000..2740dc9 --- /dev/null +++ b/t/xml-read.t @@ -0,0 +1,79 @@ +use Test::More tests => 2; +use Test::Deep; +use Data::Plist::XMLReader; + +use strict; +use warnings; + +my $xml = <<'EOXML'; +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> + +<plist version="1.0"> + <dict> + <key>Hello</key> + <string>World</string> + <key>Beast</key> + <integer>666</integer> + <key>CF$UID</key> + <integer>1</integer> + <key>Beefy</key> + <bacon><miracle /></bacon> + <key>Stuff</key> + <array> + <ustring>foo bar baz</ustring> + <real>13.37</real> + <date>1318489009</date> + <true /> + <false /> + <null /> + <array /> + <dict /> + <data>c3R1AGZm +</data> + <fill /> + </array> + </dict> +</plist> +EOXML + +my $reader = Data::Plist::XMLReader->new (); +my $data = $reader->open_string($xml); + +cmp_deeply( $data->{data}, [ 'dict' => { + 'Beefy' => [ 'bacon', [ 'miracle', ] ], + 'Beast' => [ 'integer', 666 ], + 'UID' => [ 'integer', 1 ], + 'Hello' => [ 'string', 'World' ], + 'Stuff' => [ 'array', [ + [ 'ustring', 'foo bar baz' ], + [ 'real', '13.37' ], + [ 'date', '1318489009' ], + [ 'true', 1 ], + [ 'false', 0 ], + [ 'null', undef ], + [ 'array', [] ], + [ 'dict', {} ], + [ 'data', "stu\x00ff" ], + [ 'fill', 15 ], + ]] +}]); + +cmp_deeply( $data->data, { + 'Beast' => 666, + 'Beefy' => [ 'miracle' ], + 'UID' => 1, + 'Hello' => 'World', + 'Stuff' => [ + 'foo bar baz', + '13.37', + isa('DateTime'), + 1, + 0, + undef, + [], + {}, + "stu\x00ff", + 15 + ], +}); -- 1.7.1