Link to home
Start Free TrialLog in
Avatar of sapbucket
sapbucket

asked on

need help using XML::Simple::XMLOut() and XML::Simple::XMLin()

Hello,

  I am a complete newbie when it comes to this. I would like to use XML::Simple to write a hash table to an .xml file. For me, this is best explained by examples:

the hash table is $self inside sub new():

------------------------------------------------
sub new {
      my $type = shift;
      my $class = ref ($type) || $type;
      
      my @players =();
      
      #my $refPlayers = shift;
      my $bjRules = shift;
      
      my $dealer = new BJDealer($bjRules);
      my $self = {players => \@players,
                        current_player => undef,
                        rules => $bjRules,
                        dealer => $dealer,
                        deck => undef
                     };
      
      bless($self,$class);
      return $self;
}
-----------------------------------------------------------


I try to convert using this subroutine I wrote:

-----------------------------------------------------------

sub perl_to_xml {
  my $self = shift;
 
  my $path = "c:\\testXMLOUT.xml";
  open my $fh, '>:encoding(iso-8859-1)', $path or die "open($path): $!";
  XMLout($self, OutputFile => $fh);

  return 1;
}

-----------------------------------------------------------


However, when I run the following test script:

-----------------------------------------------------------
# code fragment:
my $simul = new BJSimulation();

$simul->perl_to_xml();
-----------------------------------------------------------


I get the following error message:
Can't locate object method "print" via package "IO::Handle" at c:/Perl/site/lib/XML/Simple.pm line 540.



I'm not even sure how to diagnose the error message. I am unaccustom to seeing error messages generated by a PERL internal like IO::Handle.


Any advice would be greatly appreciated! I want to start with getting XMLOut working, and then turn to XMLIn.

Thanks!
Avatar of ozo
ozo
Flag of United States of America image

# I will try to see if I can find out why you are getting the error, but meanwhile you could do it this way insead:

  print $fh XMLout($self);
ASKER CERTIFIED SOLUTION
Avatar of ozo
ozo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of sapbucket
sapbucket

ASKER

that made it work.

i get an error when trying to open it with the Windows XP XML editor. The xml has tags that are numeric (0..number_hands), which it doesn't like. But, this seems somewhat odd to me because the source of the tags is an anonymous array nested in a hash table. Should be very common for anonymous hashes and arrays to be converted to xml, however, in this case it is generating non-XML-editor conforming xml.

(error message)
The XML page cannot be displayed
Cannot view XML input using XSL style sheet. Please correct the error and then click the Refresh button, or try again later.
--------------------------------------------------------------------------------
A name was started with an invalid character. Error processing resource 'file:///C:/testXMLOUT.xml'. Line 6, Position 8
      <0>3S</0>
-------^


in notepad it looks like this:
<opt>
  <current_player></current_player>
  <dealer current_hand="0" number_hands="1" upcard="3S">
    <bank></bank>
    <hands name="table">
      <0>3S</0>
      <0>2S</0>
    </hands>
    <rules doubleAfterPairSplit="1" doublingNumCards="0" doublingRules="2" earlySurrender="0" hitSplitAce="1" insurance="1" lateSurrender="1" natural="2" numDecks="1" penetration="100" resplitAce="1" resplitPairs="1" soft17="0" softDoubling="1" splitAce="1" splitPairs="1" ties="0" />
  </dealer>
  <deck numDecks="1" penetration="100">
    <deck>5H</deck>
    <deck>1C</deck>
    <deck>10D</deck>
    <deck>11C</deck>
    <deck>5D</deck>
    <deck>6H</deck>
    <deck>11S</deck>
    <deck>2H</deck>
    <deck>13C</deck>
    <deck>10H</deck>
    <deck>10C</deck>
    <deck>9S</deck>
    <deck>7S</deck>
    <deck>12C</deck>
    <deck>12D</deck>
    <deck>6S</deck>
    <deck>3D</deck>
    <deck>1D</deck>
    <deck>13S</deck>
    <deck>9H</deck>
    <deck>5C</deck>
    <deck>7D</deck>
    <deck>8D</deck>
    <deck>13D</deck>
    <deck>9D</deck>
    <deck>8C</deck>
    <deck>4C</deck>
    <deck>11D</deck>
    <deck>6D</deck>
    <deck>2D</deck>
    <deck>4S</deck>
    <deck>4H</deck>
    <deck>8S</deck>
    <deck>1H</deck>
    <deck>8H</deck>
    <deck>7H</deck>
    <deck>5S</deck>
    <deck>12H</deck>
    <deck>2C</deck>
    <deck>7C</deck>
    <deck>6C</deck>
    <deck>3H</deck>
    <deck>3C</deck>
    <deck>13H</deck>
    <deck>1S</deck>
    <deck>4D</deck>
    <holeCard></holeCard>
  </deck>
  <players bank="1000" buyin="1000" current_hand="0" number_hands="1" player_id="player 1">
    <hands name="table">
      <0>11H</0>
      <0>10S</0>
    </hands>
  </players>
  <players bank="500" buyin="500" current_hand="0" number_hands="1" player_id="player 2">
    <hands name="table">
      <0>12S</0>
      <0>9C</0>
    </hands>
  </players>
  <rules doubleAfterPairSplit="1" doublingNumCards="0" doublingRules="2" earlySurrender="0" hitSplitAce="1" insurance="1" lateSurrender="1" natural="2" numDecks="1" penetration="100" resplitAce="1" resplitPairs="1" soft17="0" softDoubling="1" splitAce="1" splitPairs="1" ties="0" />
</opt>
 


and the anonymous hash "object" is derived from perldoc perlreftut (a hash with anonymous arrays references (CITIES) for the values and scalars for the keys (COUNTRIES)):
package Hashtable;
use strict;

sub new {
      my $type = shift;
      my $class = ref($type) || $type;
      
      my $self={ table=>{}};
      
      bless ($self, $class);
      return ($self);
}



I think I know the solution. If you have a hash using keys that are equal to (0..number_of_keys_in_hash), than you should just use an array. But, dang it. I have all that code already written and it works great! I could convert '1' to "one" and '2' to "two", etc. Or use the $player_id as the key instead of '1','2','3', etc. And then I could have a lookup-hash that uses the $player_id as a key to find the value ('1','2','3').  That would be a subroutine called lookup_player_index().

I didn't think it would be so picky.

Any ideas on that?


XMLout($hashref) should produce conforming XML when $hashref was created by XMLin() from conforming XML,
but as you've seen, not all possible hash keys are valid XML tags.

If you want to start with an arbitrary Perl data structure,

use XML::Dumper;
print $fh pl2xml( $self );
#should produce valid XML that can be converted back with xml2pl(pl2xml( $self ));
yes that worked good for viewing the xml. That tagging system is not what my DataSet expects, unfortunately. XML::Simple was more on par with the type of XML i want to use.

I have been looking at xml books. It would seem that I should take greater care in the design of my hashtables so that the xml is structured better (and does not use invalid characters). I really am a newb at this!
Do you know how to link the XML to a VB.NET database by any chance?
OK, I figured out how to link XML to a VB.NET dataset.

It turns out that the Xml I am generating () is not compliant with W3C schema standards.

I am curious to know what people are doing with the xml generated by XML::Simple. From my point of view, it generates "garbage" xml because it is malformed (non compliant).

I wonder if there is a proper way to construct a hash so that the XML generated by XML::Simple is W3C compliant? Or maybe there is a way of setting options in XML::Simple to make it more compliant? Or maybe there is a better module than XML::Simple?

What would a "W3C Compliant" hash look like (I know, we must extend our thought here). Could there be a proper idiom for hash design that would create perfect, W3C compliant xml? What would be the algorithm for constucting such a hash? I think it would be *incredibly* useful! For me, the useful application would be that I can develop my applications using a console and perl (all text based), and when I am ready to link it to a GUI interface (like VB.NET or ASP.NET - programs that do not "glue" well with Perl). I can simply perl2xml each object hash and link the generated xml to a dataset. Since each object contains all of the data\content for that object within the hash (blessed hash reference, whatever) I get a perfect xml description of the current state of each object. As click_events occur and what not, the xml acts as a storage for next state and current state information/content to be passed between Perl and .NEt. Sound good to you? I like it because it skips the whole server/client model, AND I don't have to write any SQL statements OR deal with silly OS environment variables (so it would be platform independent too). I guess this is what XML has promised all along - I just can't make it practical with Perl yet because I can't generate compliant XML.

Any thoughts experts? I do not want to re-invent the wheel here. Maybe XML::Simple *does* create perfect, W3C compliant xml, and it is the programmer (me) that needs some re-work. Sharing your thoughts on proper hash design (in terms of XML generation) would benefit me the best.

Thanks!
perldoc XML::Simple
says:
       Caveats

       Some care is required in creating data structures which will be passed
       to "XMLout()".  Hash keys from the data structure will be encoded as
       either XML element names or attribute names.  Therefore, you should use
       hash key names which conform to the relatively strict XML naming rules:

       Names in XML must begin with a letter.  The remaining characters may be
       letters, digits, hyphens (-), underscores (_) or full stops (.).  It is
       also allowable to include one colon (:) in an element name but this
       should only be used when working with namespaces (XML::Simple can only
       usefully work with namespaces when teamed with a SAX Parser).

       You can use other punctuation characters in hash values (just not in
       hash keys) however XML::Simple does not support dumping binary data.

       If you break these rules, the current implementation of "XMLout()" will
       simply emit non-compliant XML which will be rejected if you try to read
       it back in.  (A later version of XML::Simple might take a more proac-
       tive approach).

       Note also that although you can nest hashes and arrays to arbitrary
       levels, circular data structures are not supported and will cause
       "XMLout()" to die.

       If you wish to 'round-trip' arbitrary data structures from Perl to XML
       and back to Perl, then you should probably disable array folding (using
       the KeyAttr option) both with "XMLout()" and with "XMLin()".  If you
       still don't get the expected results, you may prefer to use XML::Dumper
       which is designed for exactly that purpose.

       Refer to "WHERE TO FROM HERE?" if "XMLout()" is too simple for your
       needs.
I read that too. There should be more caveats tho. The actual (strict,compliant) method of nesting hashes and arrays should of been addressed. He makes mention of array folding, which is a good start, but should have followed up with examples.

I've got a meeting tomorrow with a guy who wrote a book on XML and VB.NET. It should lead me up to the proper idiom. I'll post here when I figure it out.

Not all of us (or should I say, none of us!) can program like you ozo. You are a rare bird indeed!
OK, this is the wrong path to take.

I am trying to have a Perl application talk to a .NET application via XML. I think better would be to use Tie::DBI or something similar. I'm going to post another question, similar intent, but completely different design pattern.

sapbucket
Thanks for the help ozo, I am giving up on xml and focusing on using relational databases (MySql) instead. Couldn't get things to work at all using xml.