Better support for special character and unicode (\n \r \" ... and \uXXXX where XXXX is an hexadecimal value)
Added features to JSON_STRING - make_json_from_string_32 (READABLE_STRING_32) - escaped_string_8: STRING_8 - escaped_string_32: STRING_32 Added associated autotests
This commit is contained in:
@@ -117,7 +117,7 @@ feature -- Access
|
|||||||
Result := jn.item.to_double
|
Result := jn.item.to_double
|
||||||
end
|
end
|
||||||
elseif attached {JSON_STRING} a_value as js then
|
elseif attached {JSON_STRING} a_value as js then
|
||||||
create {STRING_32} Result.make_from_string (js.item)
|
create {STRING_32} Result.make_from_string (js.unescaped_string_32)
|
||||||
elseif attached {JSON_ARRAY} a_value as ja then
|
elseif attached {JSON_ARRAY} a_value as ja then
|
||||||
from
|
from
|
||||||
create ll.make
|
create ll.make
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ inherit
|
|||||||
end
|
end
|
||||||
|
|
||||||
create
|
create
|
||||||
make_json
|
make_json,
|
||||||
|
make_json_from_string_32,
|
||||||
|
make_with_escaped_json
|
||||||
|
|
||||||
convert
|
convert
|
||||||
make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8})
|
make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8})
|
||||||
@@ -34,7 +36,23 @@ feature {NONE} -- Initialization
|
|||||||
require
|
require
|
||||||
item_not_void: an_item /= Void
|
item_not_void: an_item /= Void
|
||||||
do
|
do
|
||||||
item := escaped_json_string (an_item)
|
make_with_escaped_json (escaped_json_string (an_item))
|
||||||
|
end
|
||||||
|
|
||||||
|
make_json_from_string_32 (an_item: READABLE_STRING_32)
|
||||||
|
-- Initialize.
|
||||||
|
require
|
||||||
|
item_not_void: an_item /= Void
|
||||||
|
do
|
||||||
|
make_with_escaped_json (escaped_json_string_32 (an_item))
|
||||||
|
end
|
||||||
|
|
||||||
|
make_with_escaped_json (an_item: READABLE_STRING_8)
|
||||||
|
-- Initialize with an_item already escaped
|
||||||
|
require
|
||||||
|
item_not_void: an_item /= Void
|
||||||
|
do
|
||||||
|
item := an_item
|
||||||
end
|
end
|
||||||
|
|
||||||
feature -- Access
|
feature -- Access
|
||||||
@@ -42,6 +60,99 @@ feature -- Access
|
|||||||
item: STRING
|
item: STRING
|
||||||
-- Contents
|
-- Contents
|
||||||
|
|
||||||
|
unescaped_string: STRING_8
|
||||||
|
local
|
||||||
|
s: like item
|
||||||
|
i, n: INTEGER
|
||||||
|
c: CHARACTER
|
||||||
|
do
|
||||||
|
s := item
|
||||||
|
n := s.count
|
||||||
|
create Result.make (n)
|
||||||
|
from i := 1 until i > n loop
|
||||||
|
c := s[i]
|
||||||
|
if c = '\' then
|
||||||
|
if i < n then
|
||||||
|
inspect s[i+1]
|
||||||
|
when '\' then
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 2
|
||||||
|
when '%"' then
|
||||||
|
Result.append_character ('%"')
|
||||||
|
i := i + 2
|
||||||
|
when 'n' then
|
||||||
|
Result.append_character ('%N')
|
||||||
|
i := i + 2
|
||||||
|
when 'r' then
|
||||||
|
Result.append_character ('%R')
|
||||||
|
i := i + 2
|
||||||
|
when 'u' then
|
||||||
|
--| Leave unicode \uXXXX unescaped
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 1
|
||||||
|
else
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append_character (c)
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unescaped_string_32: STRING_32
|
||||||
|
local
|
||||||
|
s: like item
|
||||||
|
i, n: INTEGER
|
||||||
|
c: CHARACTER
|
||||||
|
hex: STRING
|
||||||
|
do
|
||||||
|
s := item
|
||||||
|
n := s.count
|
||||||
|
create Result.make (n)
|
||||||
|
from i := 1 until i > n loop
|
||||||
|
c := s[i]
|
||||||
|
if c = '\' then
|
||||||
|
if i < n then
|
||||||
|
inspect s[i+1]
|
||||||
|
when '\' then
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 2
|
||||||
|
when '%"' then
|
||||||
|
Result.append_character ('%"')
|
||||||
|
i := i + 2
|
||||||
|
when 'n' then
|
||||||
|
Result.append_character ('%N')
|
||||||
|
i := i + 2
|
||||||
|
when 'r' then
|
||||||
|
Result.append_character ('%R')
|
||||||
|
i := i + 2
|
||||||
|
when 'u' then
|
||||||
|
hex := s.substring (i+2, i+2+4 - 1)
|
||||||
|
if hex.count = 4 then
|
||||||
|
Result.append_code (hexadecimal_to_natural_32 (hex))
|
||||||
|
end
|
||||||
|
i := i + 2 + 4
|
||||||
|
else
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append_character ('\')
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append_character (c.to_character_32)
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
representation: STRING
|
representation: STRING
|
||||||
do
|
do
|
||||||
Result := "%""
|
Result := "%""
|
||||||
@@ -95,15 +206,116 @@ feature -- Status report
|
|||||||
|
|
||||||
feature {NONE} -- Implementation
|
feature {NONE} -- Implementation
|
||||||
|
|
||||||
escaped_json_string (s: READABLE_STRING_8): STRING
|
is_hexadecimal (s: READABLE_STRING_8): BOOLEAN
|
||||||
|
do
|
||||||
|
Result := across s as scur all scur.item.is_hexa_digit end
|
||||||
|
end
|
||||||
|
|
||||||
|
hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32
|
||||||
|
-- Hexadecimal string `s' converted to NATURAL_32 value
|
||||||
|
require
|
||||||
|
s_not_void: s /= Void
|
||||||
|
is_hexadecimal: is_hexadecimal (s)
|
||||||
|
local
|
||||||
|
i, nb: INTEGER
|
||||||
|
char: CHARACTER
|
||||||
|
do
|
||||||
|
nb := s.count
|
||||||
|
|
||||||
|
if nb >= 2 and then s.item (2) = 'x' then
|
||||||
|
i := 3
|
||||||
|
else
|
||||||
|
i := 1
|
||||||
|
end
|
||||||
|
|
||||||
|
from
|
||||||
|
until
|
||||||
|
i > nb
|
||||||
|
loop
|
||||||
|
Result := Result * 16
|
||||||
|
char := s.item (i)
|
||||||
|
if char >= '0' and then char <= '9' then
|
||||||
|
Result := Result + (char |-| '0').to_natural_32
|
||||||
|
else
|
||||||
|
Result := Result + (char.lower |-| 'a' + 10).to_natural_32
|
||||||
|
end
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
escaped_json_string (s: READABLE_STRING_8): STRING_8
|
||||||
-- JSON string with '"' and '\' characters escaped
|
-- JSON string with '"' and '\' characters escaped
|
||||||
require
|
require
|
||||||
s_not_void: s /= Void
|
s_not_void: s /= Void
|
||||||
do
|
local
|
||||||
Result := s.twin
|
i, n: INTEGER
|
||||||
Result.replace_substring_all ("\", "\\")
|
c: CHARACTER_8
|
||||||
Result.replace_substring_all ("%"", "\%"")
|
do
|
||||||
end
|
n := s.count
|
||||||
|
create Result.make (n + n // 10)
|
||||||
|
from i := 1 until i > n loop
|
||||||
|
c := s.item (i)
|
||||||
|
inspect c
|
||||||
|
when '%"' then Result.append_string ("\%"")
|
||||||
|
when '\' then Result.append_string ("\\")
|
||||||
|
when '%R' then Result.append_string ("\r")
|
||||||
|
when '%N' then Result.append_string ("\n")
|
||||||
|
else
|
||||||
|
Result.extend (c)
|
||||||
|
end
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
escaped_json_string_32 (s: READABLE_STRING_32): STRING_8
|
||||||
|
-- JSON string with '"' and '\' characters and unicode escaped
|
||||||
|
require
|
||||||
|
s_not_void: s /= Void
|
||||||
|
local
|
||||||
|
i, j, n: INTEGER
|
||||||
|
uc: CHARACTER_32
|
||||||
|
c: CHARACTER_8
|
||||||
|
h: STRING_8
|
||||||
|
do
|
||||||
|
n := s.count
|
||||||
|
create Result.make (n + n // 10)
|
||||||
|
from i := 1 until i > n loop
|
||||||
|
uc := s.item (i)
|
||||||
|
if uc.is_character_8 then
|
||||||
|
c := uc.to_character_8
|
||||||
|
inspect c
|
||||||
|
when '%"' then Result.append_string ("\%"")
|
||||||
|
when '\' then Result.append_string ("\\")
|
||||||
|
when '%R' then Result.append_string ("\r")
|
||||||
|
when '%N' then Result.append_string ("\n")
|
||||||
|
else
|
||||||
|
Result.extend (c)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Result.append ("\u")
|
||||||
|
h := uc.code.to_hex_string
|
||||||
|
-- Remove first 0 and keep 4 hexa digit
|
||||||
|
from
|
||||||
|
j := 1
|
||||||
|
until
|
||||||
|
h.count = 4 or (j <= h.count and then h.item (j) /= '0')
|
||||||
|
loop
|
||||||
|
j := j + 1
|
||||||
|
end
|
||||||
|
h := h.substring (j, h.count)
|
||||||
|
|
||||||
|
from
|
||||||
|
until
|
||||||
|
h.count >= 4
|
||||||
|
loop
|
||||||
|
h.prepend_integer (0)
|
||||||
|
end
|
||||||
|
check h.count = 4 end
|
||||||
|
Result.append (h)
|
||||||
|
end
|
||||||
|
i := i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
invariant
|
invariant
|
||||||
value_not_void: item /= Void
|
value_not_void: item /= Void
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -510,7 +510,6 @@ feature -- Test
|
|||||||
local
|
local
|
||||||
c: CHARACTER
|
c: CHARACTER
|
||||||
js: detachable JSON_STRING
|
js: detachable JSON_STRING
|
||||||
ucs: detachable STRING_32
|
|
||||||
jrep: STRING
|
jrep: STRING
|
||||||
parser: JSON_PARSER
|
parser: JSON_PARSER
|
||||||
do
|
do
|
||||||
@@ -532,9 +531,8 @@ feature -- Test
|
|||||||
js := Void
|
js := Void
|
||||||
js ?= parser.parse
|
js ?= parser.parse
|
||||||
assert ("js /= Void", js /= Void)
|
assert ("js /= Void", js /= Void)
|
||||||
ucs ?= json.object (js, Void)
|
if attached {STRING_32} json.object (js, Void) as ucs then
|
||||||
if attached ucs as l_ucs then
|
assert ("ucs.string.is_equal (%"a%")", ucs.string.is_equal ("a"))
|
||||||
assert ("ucs.string.is_equal (%"a%")", l_ucs.string.is_equal ("a"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -543,7 +541,6 @@ feature -- Test
|
|||||||
local
|
local
|
||||||
s: STRING
|
s: STRING
|
||||||
js: detachable JSON_STRING
|
js: detachable JSON_STRING
|
||||||
ucs: detachable STRING_32
|
|
||||||
jrep: STRING
|
jrep: STRING
|
||||||
parser: JSON_PARSER
|
parser: JSON_PARSER
|
||||||
do
|
do
|
||||||
@@ -565,11 +562,9 @@ feature -- Test
|
|||||||
js := Void
|
js := Void
|
||||||
js ?= parser.parse
|
js ?= parser.parse
|
||||||
assert ("js /= Void", js /= Void)
|
assert ("js /= Void", js /= Void)
|
||||||
ucs ?= json.object (js, Void)
|
if attached {STRING_32} json.object (js, Void) as l_ucs then
|
||||||
if attached ucs as l_ucs then
|
assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar"))
|
||||||
assert ("ucs.string.is_equal (%"foobar%")", ucs.string.is_equal ("foobar"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test_json_string_and_uc_string
|
test_json_string_and_uc_string
|
||||||
@@ -602,7 +597,52 @@ feature -- Test
|
|||||||
if attached ucs as l_ucs then
|
if attached ucs as l_ucs then
|
||||||
assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar"))
|
assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar"))
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test_json_string_and_special_characters
|
||||||
|
local
|
||||||
|
js: detachable JSON_STRING
|
||||||
|
s: detachable STRING_8
|
||||||
|
ucs: detachable STRING_32
|
||||||
|
jrep: STRING
|
||||||
|
parser: JSON_PARSER
|
||||||
|
do
|
||||||
|
create s.make_from_string ("foo\bar")
|
||||||
|
create js.make_json (s)
|
||||||
|
|
||||||
|
assert ("js.representation.same_string (%"%"foo\\bar%"%")", js.representation.same_string ("%"foo\\bar%""))
|
||||||
|
|
||||||
|
-- Eiffel value -> JSON value -> JSON representation with factory
|
||||||
|
js ?= json.value (s)
|
||||||
|
assert ("js /= Void", js /= Void)
|
||||||
|
if js /= Void then
|
||||||
|
assert ("js.representation.is_equal (%"%"foobar%"%")", js.representation.same_string ("%"foo\\bar%""))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- JSON representation -> JSON value -> Eiffel value
|
||||||
|
jrep := "%"foo\\bar%""
|
||||||
|
create parser.make_parser (jrep)
|
||||||
|
js ?= parser.parse
|
||||||
|
assert ("js /= Void", js /= Void)
|
||||||
|
ucs ?= json.object (js, Void)
|
||||||
|
if ucs /= Void then
|
||||||
|
assert ("ucs.same_string (%"foo\bar%")", ucs.same_string ("foo\bar"))
|
||||||
|
end
|
||||||
|
|
||||||
|
jrep := "%"foo\\bar%""
|
||||||
|
create parser.make_parser (jrep)
|
||||||
|
if attached {JSON_STRING} parser.parse as jstring then
|
||||||
|
assert ("unescaped string %"foo\\bar%" to %"foo\bar%"", jstring.unescaped_string.same_string ("foo\bar"))
|
||||||
|
end
|
||||||
|
|
||||||
|
create js.make_json_from_string_32 ({STRING_32}"%/20320/%/22909/")
|
||||||
|
assert ("escaping unicode string32 %"%%/20320/%%/22909/%" %"\u4F60\u597D%"", js.item.same_string ("\u4F60\u597D"))
|
||||||
|
|
||||||
|
jrep := "%"\u4F60\u597D%"" --| Ni hao
|
||||||
|
create parser.make_parser (jrep)
|
||||||
|
if attached {JSON_STRING} parser.parse as jstring then
|
||||||
|
assert ("same unicode string32 %"%%/20320/%%/22909/%"", jstring.unescaped_string_32.same_string ({STRING_32}"%/20320/%/22909/"))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test_json_array
|
test_json_array
|
||||||
|
|||||||
Reference in New Issue
Block a user