Subject: | encode method doesn't encode tied hashes |
I just installed JSON::XS today -- great module! :-)
However, I wanted the members of an object to be in a certain order
when encoding, so I used Tie::Hash::Indexed to enforce ordering
within the hash; just to find that not a single member was serialized.
The attached patch includes both a fix and a test case.
The primary problem is that one cannot trust the return value of
hv_iterinit() (the number of keys in a hash) when the hash is tied.
It was returning zero, so no members were serialized. Another problem
that surfaced after fixing the first one was that it's not possible
to use HeVAL() to access the SV value stored in a tied hash. This
will most probably return NULL. Instead, hv_iterval() must be used.
Marcus
Subject: | JSON-XS-1.44-tied.diff |
diff -ruN JSON-XS-1.44/MANIFEST JSON-XS-1.44-mhx/MANIFEST
--- JSON-XS-1.44/MANIFEST 2007-08-22 01:02:53.000000000 +0200
+++ JSON-XS-1.44-mhx/MANIFEST 2007-08-24 16:39:55.000000000 +0200
@@ -23,6 +23,7 @@
t/13_limit.t
t/14_latin1.t
t/15_prefix.t
+t/16_tied.t
t/99_binary.t
typemap
META.yml Module meta-data (added by MakeMaker)
diff -ruN JSON-XS-1.44/t/16_tied.t JSON-XS-1.44-mhx/t/16_tied.t
--- JSON-XS-1.44/t/16_tied.t 1970-01-01 01:00:00.000000000 +0100
+++ JSON-XS-1.44-mhx/t/16_tied.t 2007-08-24 16:28:13.000000000 +0200
@@ -0,0 +1,22 @@
+BEGIN { $| = 1; print "1..2\n"; }
+
+use JSON::XS;
+use Tie::Hash;
+use Tie::Array;
+
+our $test;
+sub ok($;$) {
+ print $_[0] ? "" : "not ", "ok ", ++$test, "\n";
+}
+
+my $js = JSON::XS->new;
+
+tie my %h, 'Tie::StdHash';
+%h = (a => 1);
+
+ok ($js->encode (\%h) eq '{"a":1}');
+
+tie my @a, 'Tie::StdArray';
+@a = (1, 2);
+
+ok ($js->encode (\@a) eq '[1,2]');
diff -ruN JSON-XS-1.44/XS.xs JSON-XS-1.44-mhx/XS.xs
--- JSON-XS-1.44/XS.xs 2007-08-13 18:18:57.000000000 +0200
+++ JSON-XS-1.44-mhx/XS.xs 2007-08-24 16:25:31.000000000 +0200
@@ -337,7 +337,7 @@
}
static void
-encode_he (enc_t *enc, HE *he)
+encode_he (enc_t *enc, HV *hv, HE *he)
{
encode_ch (enc, '"');
@@ -360,7 +360,7 @@
if (enc->json.flags & F_SPACE_BEFORE) encode_space (enc);
encode_ch (enc, ':');
if (enc->json.flags & F_SPACE_AFTER ) encode_space (enc);
- encode_sv (enc, HeVAL (he));
+ encode_sv (enc, hv_iterval (hv, he));
}
// compare hash entries, used when all keys are bytestrings
@@ -398,7 +398,20 @@
encode_ch (enc, '{'); encode_nl (enc); ++enc->indent;
- if ((count = hv_iterinit (hv)))
+ if (SvMAGICAL (hv))
+ {
+ // cannot trust hv_iterinit's return value when hash is tied
+ count = 0;
+
+ while (hv_iternext (hv))
+ ++count;
+
+ (void) hv_iterinit (hv);
+ }
+ else
+ count = hv_iterinit (hv);
+
+ if (count)
{
// for canonical output we have to sort by keys first
// actually, this is mostly due to the stupid so-called
@@ -447,7 +460,7 @@
for (i = 0; i < count; ++i)
{
encode_indent (enc);
- encode_he (enc, hes [i]);
+ encode_he (enc, hv, hes [i]);
if (i < count - 1)
encode_comma (enc);
@@ -462,7 +475,7 @@
for (;;)
{
encode_indent (enc);
- encode_he (enc, he);
+ encode_he (enc, hv, he);
if (!(he = hv_iternext (hv)))
break;