views:

359

answers:

4

I have a series of Moose objects that I'm looking to feed to JSON::XS by way of Catalyst::View::JSON. JSON::XS is unable to encode blessed data-structures. I know that there is MooseX::Storage::Format::JSON which can -- kinda -- do what I want; but, it seems pretty overly heavy. What I'm looking for is essentially the same information that XXX.pm provides. I just want the raw-data structures recursively unblessed so JSON::XS (the driver for JSON::Any that C:V:JSON uses internally) can display it.

What is the best way go about using Catalyst::View::JSON and JSON::XS with Moose objects? It seems I have four obvious choices:

  1. Make Catalyst::View::JSON work with Moose Objects, by patching C:V:JSON to read from freeze and finalize the request if the argument exposed is a Moose Object.
  2. Patch JSON::XS to fallback to return value of $obj->freeze if $obj->isa('Moose') && $obj->does('MooseX::Storage::Format::JSON'). I should look into MX:S:F:JSON to make sure that the class used by JSON::Any, and by proxy MX:S:F:JSON, is JSON::XS (hate to think of the bugs galore if JSON::Any choses a different internal encoder for the Moose object that JSON::XS is called to use.
  3. Figure out how to recursively-unbless and let Catalyst::View::JSON do its thing.
  4. Don't use Catalyst::View::JSON at all. Just write to STDOUT $obj->freeze and manually finalize requests.. This seems the most hackish.

I'm sure there are some other options, any ideas? What is my best bet?

+2  A: 

If you don't care about unblessing your object completely and making it completely unusable (from a Moose perspective), try unbless from Data::Structure::Util.

I personally prefer MooseX::Storage for a much more elegant and sustainable solution.

rodrigo
Moose has only ever guaranteed this will work in the default case. MooseX::GlobRef or MooseX::NonMoose will make this solution less than optimal for some cases.
perigrin
@perigrin!! upvoted, I've needed the functionality of MooseX::GlobRef before, I can use that today in place of some ad-hoc code to record the File::Stat before the file was opened
Evan Carroll
@rodigo, while a good idea this doesn't work. `unbless( URI->new('http://foobar.com') )`, returns a scalar reference `\"http://www.foobar.com"` which `JSON::XS` can't parse `cannot encode reference to scalar 'http://www.foobar.com' unless the scalar is 0 or 1 at -e line 1.`
Evan Carroll
+4  A: 

I tend to do option 3 using MooseX::Storage to provide a pack() method that returns a Perl data structure that I can stuff in my stash so the View can render it as it chooses.

Also note that JSON::Any can be forced (by the environment, or by passing the proper parameters to import()) to pick a specific backend. This is how the test suite works, and it is documented.

perigrin
MooseX::Storage is the only correct answer.
jrockway
@jrockway You could use the deserialization routines from KiokuDB, or write your own ... but since MooseX::Storage at this point is three or four years old and well tested in *many* production environments those aren't great solutions.
perigrin
A: 

Like it or hate it my ultimate solution was to revert back to the now deprecated JSON::Syck which does exactly what I want. Here is a quick non-Moose example of logical non-Moose serialization to JSON.

use JSON::Syck;
use URI;
use feature ':5.10';
say JSON::Syck::Dump(
 bless { foo => URI->new("http://www.evancarroll.com") }
)

I wrote the author about JSON::XS he was not game for adding the functionality. Here is some of the text (taken out of context from numerous messages to show why this feature isn't there) from Marc Lehmann the JSON::XS maintainer:

No, because you are using JSON and not Perl. Perl is obviously capable of representing any Perl data structure, but JSON is not, so you need to tell it what to do. Many people naively request something like "just dump the data structure", but JSON can't even represent all perl data structures that are non-objects.

...

Your question is like: why can't I send this 1MB jpeg image as ascii file - simply because ascii cannot represent octets.

...

Or maybe you justw ant to to throw some garbage at it and expect JSON::XS to do what is good for you (but wrong for other people) - there is simply no way for JSON::XS to magically guess what you want.

...

TO_JSON is it. Anything else is just wild guessing.

...

Then maybe you should try stuff that JSON can actually represent. Throwing some object hierarchy at it and hoping it would do "the right thing" is clearly bad software engineering - try not to resort hacks, when your protocl requires JSON, then send JSON, don't expect the json library to fix up your invalid message in the first place.

I don't know how to respond to that, other than I DON'T CARE. JUST WORK! LIKE ::SYCK. I don't expect objects to transform to JSON perfectly. But, I think I fall in the 80% that just want it to work. I'm using JSON for jQuery import not performing banking transactions during brain surgery. Ultimately, I don't want to use a special role... I want anything sent to it to magically be transformed for me to a level that makes it useful in a response to a jQuery JSON request.

UPDATE

Sorry, I missed these replies until someone said I was trolling in an unrelated medium. MooseX::Storage doesn't work for non-Moose classes, I want a general way to represent the stash in a JSON format. Unfortunately, some of those members of the stash are Moose objects. XML::Simple can do this, Data::Dumper can do this, JSON::Syck can do this, the list goes on -- I just want it done. It doesn't have to be 1:1 with Perl, and to be honest I want it done fairly close to the way JSON::Syck does it by default. My argument here then, is, "How do I get JSON::XS to work exactly like JSON::Syck does currently"? And, your answer is you can't. So I've not employed a different solution. Writing code costs money, why would I want to write to_JSON if Syck is already doing it right... I'd like to say the burden is on the nay-sayers to show what Syck is serializing a fashion that isn't desirable. Also, do keep in mind JSON::Syck was seeded by Audry, who is by no means a troll, idiot, or "brain damaged"; or, any of the other terms that are being thrown my way. I will close with this: the lack of a bad serialization route of JSON::Syck and the desired output already just-working leads me to believe this is a good choice for me. And, what could MooseX::*:JSON be doing differently with an arbitrary Moose object? Why do you think that code can't be written to accept a Moose object rather than a method on it? If you think otherwise, answer with something of substance -- I'd like to see a better response. Thanks. (directed toward @jrockway, and @Ether)

Evan Carroll
Lehmann is exactly right. And now I know why he hates users so much.Do you *really* not understand where he's coming from? Some things in Perl can't be represented as JSON. Instead of guessing, JSON::XS requires you to do the conversion. MooseX::Storage is a module that converts Moose classes to something that can be represented as JSON.
jrockway
"I don't know how to respond to that, other than I DON'T CARE. JUST WORK!"Aren't you the programmer? It's your job to make it "just work". Having "infrastructure modules" do a random guessing that works some of the time, probably maybe, sounds like something they'd do down the hall with the Perl inspired gemstone language. Sounds frustrating to me!
Ask Bjørn Hansen
"I want anything sent to it to magically be transformed for me to a level that makes it useful in a response to a jQuery JSON request."Did you look at MooseX::Storage at all?
Ask Bjørn Hansen
+1  A: 

They shouldn't have deprecated JSON::Syck, if it had functionality that wasn't replaced.

Lance Roberts
I agree, but agreeing with me is a bad idea, socially. Especially, with jerks on meta talking crap about me and linking to this post. I've gotten more serial downvotes since my meta ban than I can count.
Evan Carroll
@Evan, Yep, nothing a like a Meta link to get things going.
Lance Roberts
Yea, I regret it having come down to that, the most annoying thing is that I was banned for warning other people of censorship and despotic admins. And, now I'm forced to watch all of the guys there trash talk me, Jeff included.
Evan Carroll
@Evan, yeh, but maybe you shouldn't have trash talked them first. Life is sowing and reaping, __what comes around goes around__.
Lance Roberts
I don't trash talk anyone. Please give me just one link where I'm talking bad about someone behind their back? You should see the email that I sent Jeff, it could very well be the most polite email he has ever gotten in his whole life.
Evan Carroll
@Evan, trash talking is not about talking about someone behind their back (ref: sports), just cause you do it in their face doesn't make it not trashy. __Do you want people calling you names to your face?__
Lance Roberts
Calling names, no -- but hi-lighting problems in a civil fashion publicly online -- I can deal with that! In fact, I'd prefer that to the emotion-pandering candy coating nonsense that most seem to demand. So long as you don't ban me, I'm more than happy to debate and take my share of mud slingin'. And, I'd rather err on the side of salt-in-the-wound than censoring despotic top-down stuff meta appears to be forged from.
Evan Carroll