HeadScratcher has asked for the wisdom of the Perl Monks concerning the following question:

Hi

Given the code snippet bellow how do I order the output so the meta name is before content

Produces the following output

<meta content="J K Rolling" name="Author" /> <meta content="Harry Potter and the Philosopher's Stone" name="title" +/>

It should be

<meta name="Author" content="J K Rolling" /> <meta name="title" content="Harry Potter and the Philosopher's Stone" +/>
#!/usr/bin/perl use strict; use warnings; use HTML::TreeBuilder; use HTML::Element; my $body =HTML::TreeBuilder->new_from_file(*DATA); print $body->as_HTML('<>&',' ',{}) . "\n"; my %meta= ( "Author"=>"J K Rolling", "title","Harry Potter and the Philosopher's Stone" ); my $head = $body -> find_by_tag_name('_tag', 'head'); for my $m (sort keys %meta) { my $m_el = HTML::Element->new('meta'); $m_el->attr('name',$m); $m_el->attr('content',$meta{$m}); $head->push_content($m_el); } my $out = $body->as_HTML('<>&',' ',{}); print $out; __DATA__ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Hello World</title> </head> <body> <h1> Books by J K Rolling</h1> </body> </html>

Replies are listed 'Best First'.
Re: Ordering meta tags with HTML::Element
by Anonymous Monk on Jun 14, 2016 at 20:28 UTC
    html doesn't care about ordering, so neither does HTML::Element, and neither should you ... but if you want to change it start by copy/pasting the source of as_HTML and changing it fit your desires

      This is partially true. There are two? three? more? meta tags that are required to go first including some display related stuff and the always #1, since it affects everything that follows, <meta charset="UTF-8">.

        Elements are ordered. Attributes are not.

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Ordering meta tags with HTML::Element
by codiac (Beadle) on Jun 15, 2016 at 11:15 UTC
    Generally sub-classing is the way to go, but sub-classing HTML::Element can be pretty ugly, so not such an easy decision in this case.

    If you aren't opposed to dirty little hacks, you could modify the attribute names you make to take advantage of the "sort keys" used in the starttag sub to order the attributes.

    e.g. instead of

    $m_el->attr('name',$m); $m_el->attr('content',$meta{$m});
    you write
    $m_el->attr('aa01_name',$m); $m_el->attr('aa02_content',$meta{$m});
    Then you just post process $out to substitue in the real attribute names. e.g. $out =~ s/aa01_name/name/g

    I only feel a little dirty ;)

      Maybe a better convention would be to use leading whitespace to enforce the ordering:

      $m_el->attr(' name',$m); $m_el->attr(' content',$meta{$m});

      This way, the output would create the attribute "names" with their leading whitespace, but in the correct order. And hopefully, the receiving side won't care about the additional whitespace.

Re: Ordering meta tags with HTML::Element
by tangent (Parson) on Jun 15, 2016 at 12:45 UTC
    I am not able to reproduce your results. When I run your script it outputs in the correct order. When I change the case of the names the output changes as expected.
    my %meta = ( "Author"=>"J K Rolling", "title","Harry Potter and the Philosopher's Stone" ); Sorts: Author, title <meta content="J K Rolling" name="Author" /> <meta content="Harry Potter and the Philosopher's Stone" name="title" +/> my %meta = ( "author"=>"J K Rolling", "Title","Harry Potter and the Philosopher's Stone" ); Sorts: Title, author <meta content="Harry Potter and the Philosopher's Stone" name="Title" +/> <meta content="J K Rolling" name="author" />
    If I use an Array of Arrays to ensure the correct order I also get expected results:
    my @meta = ( ["Author"=>"J K Rolling"], ["title","Harry Potter and the Philosopher's Stone"] ); Sorts: Author, title <meta content="J K Rolling" name="Author" /> <meta content="Harry Potter and the Philosopher's Stone" name="title" +/> my @meta = ( ["author"=>"J K Rolling"], ["Title","Harry Potter and the Philosopher's Stone"] ); Sorts: author, Title <meta content="J K Rolling" name="author" /> <meta content="Harry Potter and the Philosopher's Stone" name="Title" +/>
    The docs state that push_content "adds the specified items to the end of the content list of the element". Nothing about sorting, and in your case the element is the head. There is also an unshift_content.

    I used this loop for the AoA:

    for my $m ( @meta ) { my $m_el = HTML::Element->new('meta'); $m_el->attr('name',$m->[0]); $m_el->attr('content',$m->[1]); $head->push_content($m_el); }
Re: Ordering meta tags with HTML::Element
by HeadScratcher (Novice) on Jun 15, 2016 at 07:33 UTC

    Thanx for your input so far

    I think I should clarify the the problem it's not browser, they don't care what order the meta data is or charset

    The output is read by an automated process this is failing because it requires the meta tags to be name content eg

    <meta name="Author" content="J K Rolling" /> <meta name="title" content="Harry Potter and the Philosopher's Stone" + />
    This is a mok up of the problem the actual script is over 6000 lines so just not practical to upload here
      The output is read by an automated process this is failing because it requires the meta tags to be name content

      In that case you have a problem because this "automated process" is not accepting valid HTML and pretty much any of the pre-written modules you might use (HTML::Element included) will not care about attribute ordering as has been discussed by my fellow monks already.

      The real fix here is to change the "automated process" to avoid the insistence on attribute ordering. Failling that, I guess you will need to abandon HTML::Element and fall back on hand-rolling the HTML. Good luck.

        I have no control over the automated process The only solution I have come up with is to do this but not very elegant I was hopping someone would have a better idea
        for my $m (sort keys %meta) { my $m_el = HTML::Element->new('meta'); $m_el->attr('0name',$m); $m_el->attr('1content',$meta{$m}); $head->push_content($m_el); } my $out = $body->as_HTML('<>&',' ',{}); $out=~s/0name/name/g; $out=~s/1content/content/g; print $out;
        ...hand-rolling the HTML

        Hahaha.

        Yeah. Maybe he can hire some of the neighborhood kids to help him. They probably are looking for something to do this summer.

        (Thank you for adding some humour to my day.)

      6,000 lines. I am new here. Can't you use pastebin?
        6,000 lines. I am new here. Can't you use pastebin?

        How would that help? No one reads 6000 lines. And it's off-site, probably getting lost over time. Not to mention confidentiality.

        Generally, it is much more helpful to post a short, self-contained example right here.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Ordering meta tags with HTML::Element
by anonymized user 468275 (Curate) on Jun 21, 2016 at 12:52 UTC
    You can control sorting by putting a sort routine after the sort keyword, e.g.:
    for my $m (sort nameFirst ('fred', 'alf', 'tom', 'bert', 'name', 'pete +')) { print "$m\n"; } sub nameFirst { if ($a eq 'name') { -1; } elsif ($b eq 'name') { 1; } else { 0; } }
    sort calls your routine to test a pair which arrive in your routine as $a and $b. You can return the values explicitly although it is customary to just auto-evaluate for sort routines. Returning -1 or 1 tells sort what relative position to use for a given pair. 0 means 'evaluate equal or don't care, so leave as is'. The above produces:
    name fred alf tom bert pete
    update: if you have more complex rules than this, you can drill down to an evaluation subroutine or hash. For example, suppose the order should be name first and aref last
    sub nameBlahAref { naeval($a) <=> naeval($b); } sub naeval { $_[0] eq 'name' and return 1; $_[0] eq 'aref' and return 3; return 2; }
    The <=> operator compares what is either side of it numerically and returns the required -1, 0 or 1 accordingly.

    One world, one people

Re: Ordering meta tags with HTML::Element
by Cow1337killr (Monk) on Jun 21, 2016 at 20:44 UTC
    Here is a program I threw together after surfing the Internet.
    #!/usr/bin/perl # html_indenting2.pl perl html_indenting2.pl HTML indenting wit +h Perl. # From http://stackoverflow.com/questions/8225194/html-indenting-with- +perl best answer use strict; use warnings; use HTML::HTML5::Parser qw(); use HTML::HTML5::Writer qw(); use XML::LibXML::PrettyPrint qw(); print HTML::HTML5::Writer->new( start_tags => 'force', end_tags => 'force', )->document( XML::LibXML::PrettyPrint->new_for_html( indent_string => "\t" )->pretty_print( HTML::HTML5::Parser->new->parse_string( q[<meta content="J K Rolling" name="Author" /> <meta content="Harry Potter and the Philosopher's Stone" name="title" +/> ] ) ) ); # This prints: # <!DOCTYPE html><html> # <head> # <meta content="J K Rolling" name=Author> # <meta content="Harry Potter and the Philosopher's St +one" name=title> # </head> # <body></body> # </html> __END__
    The order of the meta content is the way you want it.

      The order of the meta content is the way you want it.

      No it isn't, its same problem as in the OP, not the order that "it should be"

        Oh, dear. I'm sorry.

        I promise I will double-check and triple-check my results from now on.