XML-Fast
view release on metacpan or search on metacpan
#define MODE_ARRAYS 0x4000
typedef struct {
// config
unsigned int flags;
unsigned int bytes;
unsigned int utf8;
SV * attr;
SV * text;
SV * join;
SV * cdata;
SV * comm;
HV * array;
// state
char *encoding;
SV *encode;
int depth;
unsigned int chainsize;
xml_node * chain;
HV ** hchain;
typedef struct {
// config
unsigned int flags;
unsigned int bytes;
unsigned int utf8;
char * attr; STRLEN attl;
char * text;
char * join;
char * cdata;
char * comm;
HV * array;
// state
char *encoding;
SV *encode;
unsigned int chainsize;
xml_node * chain;
ctx->textval = 0;
}
else {
//printf("text close, store %s\n",SvPV_nolen(ctx->textval));
hv_store_a(ctx->hcurrent, ctx->text, ctx->textval);
}
ctx->textval = 0;
}
void on_cdata(void * pctx, char * data,unsigned int length) {
#if XML_DEVEL
if (!pctx) croak("Context not passed to on_cdata");
#endif
parsestate *ctx = pctx;
SV *sv = newSVpvn(data, length);
xml_sv_decode(ctx,sv);
hv_store_a(ctx->hcurrent, ctx->cdata, sv );
}
void on_pi_open(void * pctx, char * data, unsigned int length) {
#if XML_DEVEL
if (!pctx) croak("Context not passed to on_pi_open");
#endif
parsestate *ctx = pctx;
ctx->pi = newSVpvn(data,length);
}
STRLEN i, nlen;
AV *av;
SV **avv;
debug("key=%s, val=%s",key, SvPV_nolen(val));
if ( mystrcmp( key, p->text ) == 0 ) {
h2xpe(p, SvPV_nolen( val ));
}
else
if ( mystrcmp( key, p->cdata ) == 0 ) {
h2xp(p, "<![CDATA[");
h2xp(p, SvPV_nolen( val ));
h2xp(p, "]]>");
}
else
if ( p->comm && mystrcmp( key, p->comm) == 0 ) {
debug("comm: %s", SvPV_nolen( val ));
h2xp(p, "<!-- ");
h2xpe(p, SvPV_nolen( val ));
h2xp(p, " -->");
if (SvOK(*key) && SvCUR(*key) > 0 ) { // defined and length
ctx.attr = *key;
}
}
if ((key = hv_fetch(conf, "text", 4, 0)) && SvOK(*key)) {
ctx.text = *key;
}
if ((key = hv_fetch(conf, "join", 4, 0)) && SvPOK(*key)) {
ctx.join = *key;
}
if ((key = hv_fetch(conf, "cdata", 5, 0)) && SvPOK(*key)) {
ctx.cdata = *key;
}
if ((key = hv_fetch(conf, "comm", 4, 0)) && SvPOK(*key)) {
ctx.comm = *key;
}
if ((key = hv_fetch(conf, "array", 5, 0)) && SvOK(*key)) {
if (SvROK(*key) && SvTYPE( SvRV(*key) ) == SVt_PVAV) {
AV *av = (AV *) SvRV( *key );
ctx.array = newHV();
I32 len = 0, avlen = av_len(av) + 1;
SV **val;
//
} else {
state.cb.warn = on_warn;
ctx.flags |= EMIT_WARNS;
}
state.cb.die = on_die;
if(ctx.comm)
state.cb.comment = on_comment;
if(ctx.cdata)
state.cb.cdata = on_cdata;
else if(ctx.text)
state.cb.cdata = on_bytes;
state.cb.bytes = on_bytes;
state.cb.bytespart = on_bytes_part;
state.cb.uchar = on_uchar;
if (!(ctx.flags & MODE_TRIM))
state.save_wsp = 1;
}
parse(xml,&state);
ctx.attr = SvPV_nolen(*key);
// warn ("Set attr to '%s'", ctx.attr);
} else {
ctx.attr = "-";
}
if ((key = hv_fetch(conf, "text", 4, 0)) && SvPOK(*key)) {
ctx.text = SvPV_nolen(*key);
} else {
ctx.text = "#text";
}
if ((key = hv_fetch(conf, "cdata", 5, 0)) && SvPOK(*key)) {
ctx.cdata = SvPV_nolen(*key);
} else {
ctx.cdata = 0;
}
if ((key = hv_fetch(conf, "comm", 4, 0)) && SvPOK(*key)) {
ctx.comm = SvPV_nolen(*key);
} else {
ctx.comm = 0;
}
/*
if ((key = hv_fetch(conf, "array", 5, 0)) && SvOK(*key)) {
if (SvROK(*key) && SvTYPE( SvRV(*key) ) == SVt_PVAV) {
AV *av = (AV *) SvRV( *key );
# default:
xml2hash( '<item>Test1<sub />Test2</item>' )
: { item => { sub => '', '~' => 'Test1Test2' } };
xml2hash( '<item>Test1<sub />Test2</item>', join => '+' )
: { item => { sub => '', '~' => 'Test1+Test2' } };
trim [ = 1 ]
Trim leading and trailing whitespace from text nodes
cdata [ = undef ]
When defined, CDATA sections will be stored under this key
# cdata = undef
<node><![CDATA[ test ]]></node> => { node => 'test' }
# cdata = '#'
<node><![CDATA[ test ]]></node> => { node => { '#' => 'test' } }
comm [ = undef ]
When defined, comments sections will be stored under this key
When undef, comments will be ignored
# comm = undef
<node><!-- comm --><sub/></node> => { node => { sub => '' } }
lib/XML/Fast.pm view on Meta::CPAN
XSLoader::load('XML::Fast', $VERSION);
sub xml2hash($;%) {
my $xml = shift;
my %args = (
order => 0, # not impl
attr => '-', # ok
text => '#text', # ok
join => '', # ok
trim => 1, # ok
cdata => undef, # ok + fallback -> text
comm => undef, # ok
@_
);
_xml2hash($xml,\%args);
}
sub hash2xml($;%) {
my $xml = shift;
my %args = (
order => 0, # not impl
attr => '-', # ok
text => '#text', # ok
join => '', # ok
trim => 1, # ok
cdata => undef, # ok + fallback -> text
comm => undef, # ok
@_
);
_hash2xml($xml,\%args);
}
1;
__END__
=head1 NAME
lib/XML/Fast.pm view on Meta::CPAN
xml2hash( '<item>Test1<sub />Test2</item>' )
: { item => { sub => '', '~' => 'Test1Test2' } };
xml2hash( '<item>Test1<sub />Test2</item>', join => '+' )
: { item => { sub => '', '~' => 'Test1+Test2' } };
=item trim [ = 1 ]
Trim leading and trailing whitespace from text nodes
=item cdata [ = undef ]
When defined, CDATA sections will be stored under this key
# cdata = undef
<node><![CDATA[ test ]]></node> => { node => 'test' }
# cdata = '#'
<node><![CDATA[ test ]]></node> => { node => { '#' => 'test' } }
=item comm [ = undef ]
When defined, comments sections will be stored under this key
When undef, comments will be ignored
# comm = undef
<node><!-- comm --><sub/></node> => { node => { sub => '' } }
t/01-conv.t view on Meta::CPAN
our $data;
{
is_deeply
$data = xml2hash($xml1),
{root => {'-at' => 'key',nest => {'#text' => 'firstmidlast',vv => '',v => ['a',{'-at' => 'a','#text' => 'b'}]}}},
'default (1)'
or diag dd($data),"\n";
}
{
is_deeply
$data = xml2hash($xml1, cdata => '#cdata'),
{root => {'-at' => 'key',nest => {'#cdata' => 'first','#text' => 'midlast',vv => '',v => ['a',{'-at' => 'a','#text' => 'b'}]}}},
'default (1)'
or diag dd($data),"\n";
}
{
is_deeply
$data = xml2hash($xml2),
{root => {'-at' => 'key',nest => 'first & mid & last'}},
'default (2)'
or diag dd($data),"\n";
}
t/01-conv.t view on Meta::CPAN
}
{
is_deeply
$data = xml2hash(q{<root x="1">test</root>}, text => '#textnode'),
{root => { -x => 1, '#textnode' => 'test' }},
'text node'
or diag dd($data),"\n";
}
{
is_deeply
$data = xml2hash(q{<root x="1"><![CDATA[test]]></root>}, cdata => '#cdata'),
{root => { -x => 1, '#cdata' => 'test' }},
'cdata node'
or diag dd($data),"\n";
}
# Composing
# Due to unpredictable order of hash keys
# { node => { a => 1, b => 2 } }
# could be one of:
# <node><a>1</a><b>2</b></node>
# <node><b>2</b><a>1</a></node>
t/01-conv.t view on Meta::CPAN
'trim 0',
;
is
$data = hash2xml( { node => { sub => [ " \t\n", 'test' ] } }, trim => 0 ),
qq{$xml<node><sub> \t\ntest</sub></node>\n},
'trim 1',
;
}
{
is
$data = hash2xml( { node => { sub => { '@' => 'test' } } }, cdata => '@' ),
qq{$xml<node><sub><![CDATA[test]]></sub></node>\n},
'cdata @',
;
}
{
is
$data = hash2xml( { node => { sub => { '/' => 'test' } } },comm => '/' ),
qq{$xml<node><sub><!--test--></sub></node>\n},
'comm /',
;
}
t/05-wrongs.t view on Meta::CPAN
} else {
diag "died with $@";
}
pass $name;
}
}
dies_ok { xml2hash('<') } qr/Bad document end/, 'open tag';
dies_ok { xml2hash('<!') } qr/Bad document end/, 'open !';
dies_ok { xml2hash('<!--') } qr/Comment node not terminated/, 'unbalanced comment';
dies_ok { xml2hash('<![CDATA[') } qr/Cdata node not terminated/, 'unbalanced cdata';
dies_ok { xml2hash('<!DOCTYPE') } qr/Doctype not properly terminated/, 'unbalanced doctype';
dies_ok { xml2hash('<!DOCTYPE ') } qr/Doctype not properly terminated/, 'unbalanced doctype';
dies_ok { xml2hash('<!DOCTYPE[') } qr/Doctype intSubset not terminated/, 'unbalanced doctype';
dies_ok { xml2hash('<!BULLSHIT') } qr/Malformed document after/, 'bad <!';
dies_ok { xml2hash('<!BULLSHIT ') } qr/Malformed document after/, 'bad <!+';
dies_ok { xml2hash('<?') } qr/Processing instruction not terminated/, 'open PI';
dies_ok { xml2hash('<? ') } qr/Bad processing instruction/, 'open PI_';
dies_ok { xml2hash('<?x?') } qr/Processing instruction not terminated/, 'open PI';
dies_ok { xml2hash('<?x a="1" ?') } qr/Processing instruction not terminated/, 'open PI';
dies_ok { xml2hash('<?x a=b=c ?>') } qr/Error parsing PI attributes/, 'PI bad attrs';
p = search + 3;
} else xml_error("Comment node not terminated");
context->state = CONTENT_WAIT;
goto next;
} else
if ( strncmp( p, "[CDATA[", 7 ) == 0) {
context->state = CDATA_OPEN;
p+=7;
search = strstr(p,"]]>");
if (search) {
if (cb->cdata) {
cb->cdata( ctx, p, search - p);
}
p = search + 3;
} else xml_error("Cdata node not terminated");
context->state = CONTENT_WAIT;
goto next;
} else
if ( strncmp(p, "DOCTYPE", 7 ) == 0 ) {
p += 7;
//p = eat_wsp(p);
state = 0;
char *name;
char *value;
} xml_attr;
typedef void (*xml_callback)(void *,char *, unsigned int, unsigned int);
typedef struct {
void (*piopen)(void *,char *, unsigned int);
void (*piclose)(void *,char *, unsigned int);
void (*comment)(void *,char *, unsigned int);
void (*cdata)(void *,char *, unsigned int);
void (*tagopen)(void *,char *, unsigned int);
void (*attrname)(void *,char *, unsigned int);
void (*tagclose)(void *,char *, unsigned int);
void (*bytespart)(void *, char *, unsigned int);
void (*bytes)(void *, char *, unsigned int);
void (*uchar)(void *, wchar_t);
void (*warn)(void *, char *, ...);
void (*die)(void *, char *, ...);
} xml_callbacks;
( run in 0.647 second using v1.01-cache-2.11-cpan-454fe037f31 )