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
|
||||
end
|
||||
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
|
||||
from
|
||||
create ll.make
|
||||
|
||||
@@ -22,7 +22,9 @@ inherit
|
||||
end
|
||||
|
||||
create
|
||||
make_json
|
||||
make_json,
|
||||
make_json_from_string_32,
|
||||
make_with_escaped_json
|
||||
|
||||
convert
|
||||
make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8})
|
||||
@@ -34,7 +36,23 @@ feature {NONE} -- Initialization
|
||||
require
|
||||
item_not_void: an_item /= Void
|
||||
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
|
||||
|
||||
feature -- Access
|
||||
@@ -42,6 +60,99 @@ feature -- Access
|
||||
item: STRING
|
||||
-- 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
|
||||
do
|
||||
Result := "%""
|
||||
@@ -95,15 +206,116 @@ feature -- Status report
|
||||
|
||||
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
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
do
|
||||
Result := s.twin
|
||||
Result.replace_substring_all ("\", "\\")
|
||||
Result.replace_substring_all ("%"", "\%"")
|
||||
end
|
||||
local
|
||||
i, n: INTEGER
|
||||
c: CHARACTER_8
|
||||
do
|
||||
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
|
||||
value_not_void: item /= Void
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -510,7 +510,6 @@ feature -- Test
|
||||
local
|
||||
c: CHARACTER
|
||||
js: detachable JSON_STRING
|
||||
ucs: detachable STRING_32
|
||||
jrep: STRING
|
||||
parser: JSON_PARSER
|
||||
do
|
||||
@@ -532,9 +531,8 @@ feature -- Test
|
||||
js := Void
|
||||
js ?= parser.parse
|
||||
assert ("js /= Void", js /= Void)
|
||||
ucs ?= json.object (js, Void)
|
||||
if attached ucs as l_ucs then
|
||||
assert ("ucs.string.is_equal (%"a%")", l_ucs.string.is_equal ("a"))
|
||||
if attached {STRING_32} json.object (js, Void) as ucs then
|
||||
assert ("ucs.string.is_equal (%"a%")", ucs.string.is_equal ("a"))
|
||||
end
|
||||
|
||||
end
|
||||
@@ -543,7 +541,6 @@ feature -- Test
|
||||
local
|
||||
s: STRING
|
||||
js: detachable JSON_STRING
|
||||
ucs: detachable STRING_32
|
||||
jrep: STRING
|
||||
parser: JSON_PARSER
|
||||
do
|
||||
@@ -565,11 +562,9 @@ feature -- Test
|
||||
js := Void
|
||||
js ?= parser.parse
|
||||
assert ("js /= Void", js /= Void)
|
||||
ucs ?= json.object (js, Void)
|
||||
if attached ucs as l_ucs then
|
||||
assert ("ucs.string.is_equal (%"foobar%")", ucs.string.is_equal ("foobar"))
|
||||
if attached {STRING_32} json.object (js, Void) as l_ucs then
|
||||
assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar"))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
test_json_string_and_uc_string
|
||||
@@ -602,7 +597,52 @@ feature -- Test
|
||||
if attached ucs as l_ucs then
|
||||
assert ("ucs.string.is_equal (%"foobar%")", l_ucs.string.is_equal ("foobar"))
|
||||
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
|
||||
|
||||
test_json_array
|
||||
|
||||
Reference in New Issue
Block a user