NAME

    XML::Chain - chained way of manipulating and inspecting XML documents

SYNOPSIS

        use XML::Chain qw(xc);
    
        # basics
        my $div = xc('div', class => 'pretty')
                    ->c('h1')->t('hello')
                    ->up
                    ->c('p', class => 'intro')->t('world')
                    ->root
                    ->a( xc('p')->t('of chained XML.') );
        say $div->as_string;
        # <div class="pretty"><h1>hello</h1><p class="intro">world</p><p>of chained XML.</p></div>
    
        my $sitemap =
            xc('urlset', xmlns => 'http://www.sitemaps.org/schemas/sitemap/0.9')
            ->t("\n")
            ->c('url')
                ->a('loc',        '-' => 'https://metacpan.org/pod/XML::Chain::Selector')
                ->a('lastmod',    '-' => DateTime->from_epoch(epoch => 1507451828)->strftime('%Y-%m-%d'))
                ->a('changefreq', '-' => 'monthly')
                ->a('priority',   '-' => '0.6')
            ->up->t("\n")
            ->c('url')
                ->a('loc',        '-' => 'https://metacpan.org/pod/XML::Chain::Element')
                ->a('lastmod',    '-' => DateTime->from_epoch(epoch => 1507279028)->strftime('%Y-%m-%d'))
                ->a('changefreq', '-' => 'monthly')
                ->a('priority',   '-' => '0.5')
            ->up->t("\n");
        say $sitemap->as_string;
        # <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
        # <url><loc>https://metacpan.org/pod/XML::Chain::Selector</loc><lastmod>2017-10-08</lastmod><changefreq>monthly</changefreq><priority>0.6</priority></url>
        # <url><loc>https://metacpan.org/pod/XML::Chain::Element</loc><lastmod>2017-10-06</lastmod><changefreq>monthly</changefreq><priority>0.5</priority></url>
        # </urlset>

DESCRIPTION

    This module provides fast and easy way to create and manipulate XML
    elements via set of chained method calls.

EXPORTS

 xc

    Exported factory method creating new XML::Chain::Element object with a
    document element as provided in parameters. For example:

        my $icon = xc('i', class => 'icon-download icon-white');
        # <i class="icon-download icon-white"/>

    See also "c, append_and_current" in XML::Chain::Selector, from which
    XML::Chain::Element inherits all methods, for the element parameter
    description and "CHAINED METHODS" in XML::Chain::Selector for methods
    of returned object.

  xc($el_name, @attrs) scalar with 1+ arguments

    Element with $el_name will be create as document element and  @attrs 
    will be added to it in the same order.

    In case of hash reference passed as argument, key + values will be set
    as attributes, in alphabetical sorted key name order.

    Attribute name "-" is a special case and the value will used for text
    content inside the element.

  xc($xml_libxml_ref)

    In case of XML::LibXML, it will be set as document element.

  xc($what_ref)

    Any other reference will be passed to "slurp($what)" in IO::Any which
    will be then parsed by "load_xml" in XML::LibXML and result set as
    document element.

        say xc([$tmp_dir, 't01.xml'])->as_string
        say xc(\'<body><h1>and</h1><h1>head</h1></body>')
                ->find('//h1')->count

  xc($scalar)

    Element with $scalar will be create as document element.

        say xc('body');

CHAINED METHODS, METHODS and ELEMENT METHODS

    See XML::Chain::Selector and XML::Chain::Element.

CHAINED DOCUMENT METHODS

        xc('body')->t('save me')->set_io_any([$tmp_dir, 't01.xml'])->store;
        # $tmp_dir/t01.xml file now consists of:
            <body>save me</body>
        xc([$tmp_dir, 't01.xml'])->empty->c('div')->t('updated')->store;
        # $tmp_dir/t01.xml file now consists of:
            <body><div>updated</div></body>

 set_io_any

    Store  $what , $options  of IO::Any for future use with  ->store() 

 store

    Calls  IO::Any-spew($io_any, $self->as_string, {atomic => 1}) > to save
    XML back it it's original file of the the target set via set_io_any.

TODO

        - partial/special tidy (on elements inside xml)
        - per ->data() storage
        - ->each(sub {...}) / ->map(sub {}) / ->grep(sub {})
        - setting and handling namespaces and elements with ns prefixes
        - ~ton of selectors and manipulators to be added

CONTRIBUTORS & CREDITS

    Initially inspired by Strophe.Builder, then also by jQuery.

    The following people have contributed to the XML::Chain by committing
    their code, sending patches, reporting bugs, asking questions,
    suggesting useful advice, nitpicking, chatting on IRC or commenting on
    my blog (in no particular order):

        Slaven Rezic
        Vienna.pm (for listening to my talk and providing valuable feedback)
        Mohammad S Anwar
        you?

    Also thanks to my current day-job-employer http://geizhals.at/.

BUGS

    Please report any bugs or feature requests via
    https://github.com/meon/XML-Chain/issues.

AUTHOR

    Jozef Kutej

COPYRIGHT & LICENSE

    Copyright 2017 Jozef Kutej, all rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.


----------------------------------------------------------------------------
NAME

    XML::Chain::Selector - selector for traversing the XML::Chain

SYNOPSIS

        my $user = xc('user', xmlns => 'http://testns')->auto_indent({chars=>' 'x4})
            ->a('name', '-' => 'Johnny Thinker')
            ->a('username', '-' => 'jt')
            ->c('bio')
                ->c('div', xmlns => 'http://www.w3.org/1999/xhtml')
                    ->a('h1', '-' => 'about')
                    ->a('p', '-' => '...')
                    ->up
                ->a('greeting', '-' => 'Hey')
                ->up
            ->a('active', '-' => '1')
            ->root;
        say $user->as_string;

    Will print:

        <user xmlns="http://testns">
            <name>Johnny Thinker</name>
            <username>jt</username>
            <bio>
                <div xmlns="http://www.w3.org/1999/xhtml">
                    <h1>about</h1>
                    <p>...</p>
                </div>
                <greeting>Hey</greeting>
            </bio>
            <active>1</active>
        </user>

DESCRIPTION

CHAINED METHODS

 c, append_and_select

    Appends new element to current elements and changes context to them.
    New element is defined in parameters:

        $xc->c('i', class => 'icon-download icon-white')
        # <i class="icon-download icon-white"/>

    First parameter is name of the element, then followed by optional
    element attributes.

 t, append_text

    Appends text to current elements.

        xc('span')->t('some')->t(' ')->t('more text')
        # <span>some more text</span>

    First parameter is name of the element, then followed by optional
    element attributes.

 root

    Sets document element as current element.

        say xc('p')
            ->t('this ')
            ->a(xc('b')->t('is'))
            ->t(' important!')
            ->root->as_string;
        # <p>this <b>is</b> important!</p>

 up, parent

    Traverse current elements and replace them by their parents.

 find

        say $xc->find('//p/b[@class="less"]')->text_content;
        say $xc->find('//xhtml:div', xhtml => 'http://www.w3.org/1999/xhtml')->count;

    Look-up elements by xpath and set them as current elements. Optional
    look-up namespace prefixes can be specified. Any global registered
    namespace prefixes "reg_global_ns" can be used.

 children

    Set all current elements child nodes as current elements.

 first

    Set first current elements as current elements.

 empty

    Removes all child nodes from current elements.

 rename

        my $body = xc('bodyz')->rename('body');
        # <body/>

    Rename node name(s).

 attr

        my $img = xc('img')->attr('href' => '#', 'title' => 'imaget');
        # <img href="#" title="imaget"/>
    
        say $img->attr('title')
        # imaget
    
        say $img->attr('title' => undef)
        # <img href="#"/>

    Get or set elements attributes. With one argument returns attribute
    value otherwise sets them. Setting attribute to undef will remove it
    from the element.

 each

        # rename using each
        $body->rename('body');
        $body
            ->a(xc('p.1')->t(1))
            ->a(xc('p.2')->t(2))
            ->a(xc('div')->t(3))
            ->a(xc('p.3')->t(4))
            ->each(sub { $_->rename('p') if $_->name =~ m/^p[.]/ });
        is($body, '<body><p>1</p><p>2</p><div>3</div><p>4</p></body>','rename using each()');

    Loops through all selected elements and calls callback for each of
    them.

 remap

        xc('body')->a('p', i => 1)->children->remap(
            sub {
                (map {xc('e', i => $_)} 1 .. 3), $_;
            }
        )->root;
        # <body><e i="1"/><e i="2"/><e i="3"/><p i="1"/></body>

    Replaces all selected elements by callback returned elements.

 rm, remove_and_parent

        my $pdiv = xc('base')
                ->a(xc('p')->t(1))
                ->a(xc('p')->t(2))
                ->a(xc('div')->t(3))
                ->a(xc('p')->t(4));
        my $p = $pdiv->find('//p');
        # $pdiv->find('//p[position()=3]')->rm->name eq 'base'
        # $p->count == 2     # deleted elements are skipped also in old selectors
        # <base><p>1</p><p>2</p><div>3</div></base>

    Deletes current elements and returns their parent.

 auto_indent

    (experimental feature (good/usefull for debug, needs more testing),
    works only on element for which as_string is called at this moment)

        my $simple = xc('div')
                        ->auto_indent(1)
                        ->a('div', '-' => 'in1')
                        ->a('div', '-' => 'in2')
                        ->t('in2.1')
                        ->a('div', '-' => 'in3')
        ;
        say $simple->as_string;

    Will print:

        <div>
            <div>in1</div>
            <div>in2</div>
            in2.1
            <div>in3</div>
        </div>

    Turn on/off tidy/auto-indentation of document elements. Default
    indentation characters are tabs.

    Argument can be either true/false scalar or a hashref with indentation
    options. Currently	{chars=' 'x4} > will set indentation characters to
    be four spaces.

CHAINED DOCUMENT METHODS

    See "CHAINED DOCUMENT METHODS" in XML::Chain.

METHODS

 as_string, toString

    Returns string representation of current XML elements. Call root before
    to get a string representing the whole document.

        $xc->as_string
        $xc->root->as_string

 as_xml_libxml

    Returns array of current elements as XML::LibXML objects.

 text_content

    Returns text content of all current XML elements.

 count / size

        say $xc->find('//b')->count;

    Return the number of current elements.

 single

        my $lxml_el = $xc->find('//b')->first->as_xml_libxml;

    Checks is there is exactly one element in current elements and return
    it as XML::Chain::Element object.

 reg_global_ns

        $sitemap->reg_global_ns('i' => 'http://www.google.com/schemas/sitemap-image/1.1');
        $sitemap->reg_global_ns('s' => 'http://www.sitemaps.org/schemas/sitemap/0.9');
        say $sitemap->find('/s:urlset/s:url/i:image')->count
        # 2

AUTHOR

    Jozef Kutej

COPYRIGHT & LICENSE

    Copyright 2017 Jozef Kutej, all rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.


----------------------------------------------------------------------------
NAME

    XML::Chain::Element - helper class for XML::Chain representing single
    element

SYNOPSIS

        xc('body')->c(h1)->t('title')->root

DESCRIPTION

    Returned by "single" in XML::Chain::Selector call.

METHODS

 name

    return element name

 as_xml_libxml

    Returns XML::LibXML::Element object.

 XML::Chain::Selector methods

    All of the XML::Chain::Selector methods works too.

AUTHOR

    Jozef Kutej

COPYRIGHT & LICENSE

    Copyright 2017 Jozef Kutej, all rights reserved.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

