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

Brethren,

I have a HoH structure—actually a mix of hashes and arrays. It's created by reading an XML file, using XML::Simple. If I bless the interior hashes, am I going to mess up:

In more detail:
So a typical InspectionData structure read in from the XML looks something like
HASH(0xb82b64) 'InspectionRecord' => ARRAY(0xc207f0) 0 HASH(0xb82be8) 'FileRecord' => ARRAY(0xc20790) 0 HASH(0xb82d08) 'filename' => 'yelloJello.pl' 'Inspector' => ARRAY(0xc3cfc8) 0 HASH(0xb82c84) 'user' => 'smith_j' 'date' => '12-AUG-2007' 'fdnum' => 'D0000'
If I bless the interior hashes that represent InspectionData, FileRecord and Inspector into their own classes, would/should that give indigestion to the XMLout routine? Would it represent a sin against Object Oriented design or otherwise be a venial sin?

XML::Simple stores attributes as hash references and child entities as array references (if you've used the recommended 'ForceArray' option, which I have.) There are a couple places in my code where I walk the key/value pairs of each HoH, testing to see if the value is an arrayref or a hashref:

my $type = ref $hohref; if(not $type){ ....} elsif($type eq 'ARRAY'){ ....} elsif($type eq 'HASH'){ ....} else{ ....}
If I understand rightly, once I've blessed the InspectionRecord etc. $type isn't going to get set to HASH anymore - it's going to come back as InspectionRecord. The code that does this test isn't specific to the Inspection project; it's supposed to be general to anything read in by XML::Simple. So I'm reluctant to added tests there to see if $type is a InspectionRecord, FileRecord, Inspector...

What I want to test for is like Is the underlying implementation of this blessed thing a hashref? This seems contrary to OO principals, but I need to do it to keep the existing code running—and, I suspect, to dump back out to XML.

Am I going about this the right way?

In summary, Randall Schwartz said

The bless operation puts a little post-it note on the hash data structure (not the reference!) that says ``I belong to Widget''.
How do I keep from choking on the post-it note?

throop

Replies are listed 'Best First'.
Re: Blessing interior hashes
by ikegami (Patriarch) on Oct 03, 2007 at 16:40 UTC

    What I want to test for is like Is the underlying implementation of this blessed thing a hashref?

    reftype($ref) eq 'HASH' answers that question.
    reftype can be found in core module Scalar::Util.

    UNIVERSAL::isa($ref, 'HASH') also answers that question.
    isa is always present. It's documented in core module UNIVERSAL.

    ...but is it the right question? Shouldn't you be asking "Can this ref be used as a hashref?"

    eval { %$ref || 1 } will answer that question.

    if (eval { %$ref || 1 }) { # Hash ... } elsif (eval { @$ref || 1 }) { # Array ... } else { ... }

    Update: Added isa.

      Beware that eval { %$ref } may emit a warning.

      my $ref = [{}]; eval { %$ref || 1 }; __END__ Pseudo-hashes are deprecated at ...

      lodin

        Good catch. In the presented if/elsif scenario, you can avoid this by checking for arrays first.
      Thanks, ikegami. I'll use that.

      I was initially puzzled—what is going inside those eval's? Let me see if I've puzzled it out right:

      • If $ref is a reference to a hash, then %$ref will eval to something legal.
        • It's possibly an empty hash.
        • The '|| 1' assures a true value.
      • If $ref is not a reference to a hash or something that can be used as a hash, then
        • an error occurs.
        • eval returns undef.
        • $@ would contain an error message if we checked it (but we don't because the undef told us all we need to know.)
      Right?

      throop

        Yup, excellent!

        Two little additions:
        || 1 assures a true value when %$ref is an empty hash.
        || 1 avoids the "void context" warning ; 1 would issue.

        One little correction:
        On error, eval returns false (dualvar 0, ""), not undef.

Re: Blessing interior hashes
by kyle (Abbot) on Oct 03, 2007 at 16:50 UTC

    I'm not completely sure, but after a quick look at XMLout in XML::Simple, it seems that it tests things this way:

    # Handle arrayrefs elsif(UNIVERSAL::isa($ref, 'ARRAY')) {

    That will see $ref as the array that it is, even if it's blessed into some class. You can do this in your own code as well, or you can use the reftype method in Scalar::Util.

    I can't tell you for sure that XML::Simple is that careful everywhere, so I don't know that blessing some structure that you pass to it won't cause any problems, but that's the way I'd bet. It seems clear that the code has already been written to account for that, at least in some cases, so hopefully it's everywhere.