From f1c8fb3404b1633013f1c76587e7442112bc13bb Mon Sep 17 00:00:00 2001 From: gnosygnu Date: Sun, 13 Jan 2019 17:30:27 -0500 Subject: [PATCH] Scribunto: Allow strings and other primitive values for jsonEncode / jsonDecode [#329] --- .../xowa/htmls/core/Xow_hdump_mgr__save.java | 6 +- .../xtns/scribunto/libs/Scrib_lib_text.java | 70 +++++++++++++++++++ .../libs/Scrib_lib_text_json_tst.java | 24 +++++-- 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/400_xowa/src/gplx/xowa/htmls/core/Xow_hdump_mgr__save.java b/400_xowa/src/gplx/xowa/htmls/core/Xow_hdump_mgr__save.java index 3a0430063..5ef7b7616 100644 --- a/400_xowa/src/gplx/xowa/htmls/core/Xow_hdump_mgr__save.java +++ b/400_xowa/src/gplx/xowa/htmls/core/Xow_hdump_mgr__save.java @@ -19,7 +19,6 @@ import gplx.core.ios.*; import gplx.core.primitives.*; import gplx.xowa.wikis.da import gplx.xowa.addons.wikis.pages.syncs.core.parsers.*; public class Xow_hdump_mgr__save { private final Xow_wiki wiki; private final Xoh_hzip_mgr hzip_mgr; private final Io_stream_zip_mgr zip_mgr; - private final Xosync_hdoc_parser plain_parser = new Xosync_hdoc_parser(); private final Xoh_page tmp_hpg; private final Xoh_hzip_bfr tmp_bfr = Xoh_hzip_bfr.New_txt(32); private Bool_obj_ref html_db_is_new = Bool_obj_ref.n_(); private int dflt_zip_tid, dflt_hzip_tid; public Xow_hdump_mgr__save(Xow_wiki wiki, Xoh_hzip_mgr hzip_mgr, Io_stream_zip_mgr zip_mgr, Xoh_page tmp_hpg) { @@ -52,11 +51,14 @@ public class Xow_hdump_mgr__save { private byte[] Write(Xoh_hzip_bfr bfr, Xow_wiki wiki, Xoae_page page, Xoh_page hpg, Xoh_hzip_mgr hzip_mgr, Io_stream_zip_mgr zip_mgr, int zip_tid, int hzip_tid, byte[] src) { switch (hzip_tid) { case Xoh_hzip_dict_.Hzip__none: - src = plain_parser.Parse_hdoc(wiki.Domain_itm(), page.Url_bry_safe(), hpg.Hdump_mgr().Imgs(), src); break; case Xoh_hzip_dict_.Hzip__v1: src = hzip_mgr.Encode_as_bry((Xoh_hzip_bfr)bfr.Clear(), wiki, hpg, src); break; + // TOMBSTONE: not used; Xosync_update_mgr calls save directly; unsure if this should be restored for parallelism + // case Xoh_hzip_dict_.Hzip__plain: + // src = plain_parser.Parse_hdoc(wiki.Domain_itm(), page.Url_bry_safe(), hpg.Hdump_mgr().Imgs(), src); + // break; } src_as_hzip = src; if (zip_tid > gplx.core.ios.streams.Io_stream_tid_.Tid__raw) diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text.java index 08cd713e6..5c6b53bd8 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text.java @@ -66,6 +66,24 @@ public class Scrib_lib_text implements Scrib_lib { public boolean JsonEncode(Scrib_proc_args args, Scrib_proc_rslt rslt) { Object itm = args.Pull_obj(0); + // check if json is primitive, and return that; see NOTE below; PAGE:en.w:Template:Format_TemplateData ISSUE#:301; DATE:2019-01-13 + int itm_type_id = Type_ids_.To_id_by_obj(itm); + switch (itm_type_id) { + case Type_ids_.Id__bool: + case Type_ids_.Id__byte: + case Type_ids_.Id__short: + case Type_ids_.Id__int: + case Type_ids_.Id__long: + case Type_ids_.Id__float: + case Type_ids_.Id__double: + case Type_ids_.Id__char: + case Type_ids_.Id__str: + case Type_ids_.Id__bry: + case Type_ids_.Id__date: + case Type_ids_.Id__decimal: + return rslt.Init_obj(itm); + } + // try to determine if node or array; EX: {a:1, b:2} vs [a:1, b:2] Keyval[] itm_as_nde = null; Object itm_as_ary = null; @@ -113,6 +131,45 @@ public class Scrib_lib_text implements Scrib_lib { public boolean JsonDecode(Scrib_proc_args args, Scrib_proc_rslt rslt) { // init byte[] json = args.Pull_bry(0); + + // check if json is primitive, and return that; see NOTE below; PAGE:en.w:Template:Format_TemplateData ISSUE#:301; DATE:2019-01-13 + int json_len = json.length; + boolean is_json_like = false; + boolean is_numeric = true; + for (int i = 0; i < json_len; i++) { + byte json_byte = json[i]; + switch (json_byte) { + case Byte_ascii.Brack_bgn: + case Byte_ascii.Brack_end: + case Byte_ascii.Curly_bgn: + case Byte_ascii.Curly_end: + is_json_like = true; + is_numeric = false; + i = json_len; + break; + case Byte_ascii.Num_0: case Byte_ascii.Num_1: case Byte_ascii.Num_2: case Byte_ascii.Num_3: case Byte_ascii.Num_4: + case Byte_ascii.Num_5: case Byte_ascii.Num_6: case Byte_ascii.Num_7: case Byte_ascii.Num_8: case Byte_ascii.Num_9: + break; + default: + is_numeric = false; + break; + } + } + if (!is_json_like) { + if (is_numeric) { + return rslt.Init_obj(Bry_.To_int(json)); + } + else { + if (Bry_.Eq(json, Bool_.True_bry)) + return rslt.Init_obj(true); + else if (Bry_.Eq(json, Bool_.False_bry)) + return rslt.Init_obj(false); + else { + return rslt.Init_obj(json); + } + } + } + int flags = args.Cast_int_or(1, 0); int opts = Scrib_lib_text__json_util.Opt__force_assoc; if (Bitmask_.Has_int(flags, Scrib_lib_text__json_util.Flag__try_fixing)) @@ -156,3 +213,16 @@ public class Scrib_lib_text implements Scrib_lib { return String_.new_u8(msg_mgr.Val_by_key_obj(Bry_.new_u8(msg_key))); } } +/* +jsonDecode + +NOTE: this code is adhoc; MW calls PHP's jsonDecode +jsonDecode has very liberal rules for decoding which seems to include +* auto-converting bools and ints from strings +* throwing syntax errors if text looks like JSON but is not + +This code emulates some of the above rules + +REF: http://php.net/manual/en/function.json-decode.php +REF: https://doc.wikimedia.org/mediawiki-core/master/php/FormatJson_8php_source.html +*/ diff --git a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text_json_tst.java b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text_json_tst.java index b99fcaa2e..7e33a7262 100644 --- a/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text_json_tst.java +++ b/400_xowa/src/gplx/xowa/xtns/scribunto/libs/Scrib_lib_text_json_tst.java @@ -14,7 +14,8 @@ GPLv3 License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-GPLv3.txt Apache License: https://github.com/gnosygnu/xowa/blob/master/LICENSE-APACHE2.txt */ package gplx.xowa.xtns.scribunto.libs; import gplx.*; import gplx.xowa.*; import gplx.xowa.xtns.*; import gplx.xowa.xtns.scribunto.*; -import org.junit.*; import gplx.langs.jsons.*; +import org.junit.*; import gplx.core.tests.*; +import gplx.langs.jsons.*; public class Scrib_lib_text_json_tst { private Scrib_invoke_func_fxt fxt = new Scrib_invoke_func_fxt(); private Scrib_lib_text lib; private Scrib_lib_json_fxt json_fxt = new Scrib_lib_json_fxt(); @@ -296,6 +297,12 @@ public class Scrib_lib_text_json_tst { Tfds.Eq(kv_ary[0].Key_as_obj(), 1); Tfds.Eq(((Keyval[])kv_ary[0].Val())[0].Key_as_obj(), 11); } + @Test public void Primitives() { // NOTE: based on MW; ISSUE#:329; DATE:2019-01-13 + json_fxt.Test_json_roundtrip_primitive(fxt, lib, "abc", "abc"); + json_fxt.Test_json_roundtrip_primitive(fxt, lib, true, "true"); + json_fxt.Test_json_roundtrip_primitive(fxt, lib, false, "false"); + json_fxt.Test_json_roundtrip_primitive(fxt, lib, 123, "123"); + } } class Scrib_lib_json_fxt { private final Json_wtr wtr = new Json_wtr(); @@ -303,6 +310,12 @@ class Scrib_lib_json_fxt { Test_json_decode(fxt, lib, Scrib_lib_text__json_util.Flag__none, json, obj); Test_json_encode(fxt, lib, Scrib_lib_text__json_util.Flag__none, obj, json); } + public void Test_json_roundtrip_primitive(Scrib_invoke_func_fxt fxt, Scrib_lib_text lib, Object obj, String expd_encoded) { + Object actl_encoded = fxt.Test_scrib_proc_rv_as_obj(lib, Scrib_lib_text.Invk_jsonEncode, Object_.Ary(obj, Scrib_lib_text__json_util.Flag__none)); + Gftest.Eq__str(Object_.Xto_str_loose_or(actl_encoded, "failed"), expd_encoded); + Object actl_decoded = fxt.Test_scrib_proc_rv_as_obj(lib, Scrib_lib_text.Invk_jsonDecode, Object_.Ary(expd_encoded, Scrib_lib_text__json_util.Flag__none)); + Gftest.Eq__str(Object_.Xto_str_loose_or(obj, "failed"), Object_.Xto_str_loose_or(actl_decoded, "failed")); + } public Object Test_json_decode(Scrib_invoke_func_fxt fxt, Scrib_lib_text lib, int flag, String raw, Object expd) { Object actl = fxt.Test_scrib_proc_rv_as_obj(lib, Scrib_lib_text.Invk_jsonDecode, Object_.Ary(raw, flag)); Tfds.Eq_str_lines(To_str(expd), To_str(actl), raw); @@ -314,8 +327,11 @@ class Scrib_lib_json_fxt { private String To_str(Object o) { if (o == null) return "<< NULL >>"; Class type = o.getClass(); - if (Type_.Eq(type, Keyval[].class)) return Kv_ary_utl.Ary_to_str(wtr, (Keyval[])o); - else if (Type_.Is_array(type)) return Array_.To_str_nested_obj(o); - else return Object_.Xto_str_strict_or_null(o); + if (Type_.Eq(type, Keyval[].class)) + return Kv_ary_utl.Ary_to_str(wtr, (Keyval[])o); + else if (Type_.Is_array(type)) + return Array_.To_str_nested_obj(o); + else + return Object_.Xto_str_strict_or_null(o); } }