X-Git-Url: http://nitlanguage.org diff --git a/lib/json/static.nit b/lib/json/static.nit index fdb9f65..e333b85 100644 --- a/lib/json/static.nit +++ b/lib/json/static.nit @@ -31,6 +31,9 @@ private import json_lexer interface Jsonable # Encode `self` in JSON. # + # This is a recursive method which can be refined by any subclasses. + # To write any `Serializable` object to JSON, see `serialize_to_json`. + # # SEE: `append_json` fun to_json: String is abstract @@ -47,9 +50,9 @@ interface Jsonable # avoid cyclic references between `append_json` and `to_json` when none are # implemented. protected fun to_json_by_append: String do - var buffer = new RopeBuffer + var buffer = new FlatBuffer append_json(buffer) - return buffer.write_to_string + return buffer.to_s end # Append the JSON representation of `self` to the specified buffer. @@ -80,7 +83,7 @@ interface Jsonable var res = new FlatBuffer pretty_json_visit(res, 0) res.add '\n' - return res.write_to_string + return res.to_s end private fun pretty_json_visit(buffer: FlatBuffer, indent: Int) is abstract @@ -89,32 +92,40 @@ end redef class Text super Jsonable + # Removes JSON-escaping if necessary in a JSON string + # + # assert "\\\"string\\uD83D\\uDE02\\\"".unescape_json == "\"string😂\"" + fun unescape_json: Text do + if not json_need_escape then return self + return self.json_to_nit_string + end + + # Does `self` need treatment from JSON to Nit ? + # + # i.e. is there at least one `\` character in it ? + # + # assert not "string".json_need_escape + # assert "\\\"string\\\"".json_need_escape + protected fun json_need_escape: Bool do return has('\\') + redef fun append_json(buffer) do buffer.add '\"' - for i in [0..self.length[ do + for i in [0 .. self.length[ do var char = self[i] if char == '\\' then buffer.append "\\\\" else if char == '\"' then buffer.append "\\\"" - else if char == '\/' then - buffer.append "\\/" - else if char < 16.ascii then + else if char < ' ' then if char == '\n' then buffer.append "\\n" else if char == '\r' then buffer.append "\\r" else if char == '\t' then buffer.append "\\t" - else if char == 0x0C.ascii then - buffer.append "\\f" - else if char == 0x08.ascii then - buffer.append "\\b" else - buffer.append "\\u000{char.ascii.to_hex}" + buffer.append char.escape_to_utf16 end - else if char < ' ' then - buffer.append "\\u00{char.ascii.to_hex}" else buffer.add char end @@ -122,13 +133,65 @@ redef class Text buffer.add '\"' end + # Escapes `self` from a JSON string to a Nit string + # + # assert "\\\"string\\\"".json_to_nit_string == "\"string\"" + # assert "\\nEscape\\t\\n".json_to_nit_string == "\nEscape\t\n" + # assert "\\u0041zu\\uD800\\uDFD3".json_to_nit_string == "Azu𐏓" + protected fun json_to_nit_string: String do + var res = new FlatBuffer.with_capacity(bytelen) + var i = 0 + var ln = self.length + while i < ln do + var char = self[i] + if char == '\\' then + i += 1 + char = self[i] + if char == 'b' then + char = 0x08.code_point + else if char == 'f' then + char = 0x0C.code_point + else if char == 'n' then + char = '\n' + else if char == 'r' then + char = '\r' + else if char == 't' then + char = '\t' + else if char == 'u' then + var u16_esc = from_utf16_digit(i + 1) + char = u16_esc.code_point + if char.is_surrogate and i + 10 < ln then + if self[i + 5] == '\\' and self[i + 6] == 'u' then + u16_esc <<= 16 + u16_esc += from_utf16_digit(i + 7) + char = u16_esc.from_utf16_surr.code_point + i += 6 + else + char = 0xFFFD.code_point + end + end + i += 4 + end + # `"`, `/` or `\` => Keep `char` as-is. + end + res.add char + i += 1 + end + return res.to_s + end + + # Encode `self` in JSON. # # ~~~ # assert "\t\"http://example.com\"\r\n\0\\".to_json == - # "\"\\t\\\"http:\\/\\/example.com\\\"\\r\\n\\u0000\\\\\"" + # "\"\\t\\\"http://example.com\\\"\\r\\n\\u0000\\\\\"" # ~~~ - redef fun to_json do return to_json_by_append + redef fun to_json do + var b = new FlatBuffer.with_capacity(bytelen) + append_json(b) + return b.to_s + end # Parse `self` as JSON. # @@ -175,6 +238,16 @@ redef class Text end end +redef class FlatText + redef fun json_need_escape do + var its = items + for i in [first_byte .. last_byte] do + if its[i] == 0x5Cu8 then return true + end + return false + end +end + redef class Buffer # Append the JSON representation of `jsonable` to `self`. @@ -369,6 +442,11 @@ redef class JsonParseError "\"position\":{position.to_json}," + "\"message\":{message.to_json}\}" end + + redef fun pretty_json_visit(buf, indents) do + buf.clear + buf.append(to_json) + end end redef class Position @@ -426,41 +504,7 @@ end redef class Nstring # The represented string. - private fun to_nit_string: String do - var res = new FlatBuffer - var i = 1 - while i < text.length - 1 do - var char = text[i] - if char == '\\' then - i += 1 - char = text[i] - if char == 'b' then - char = 0x08.ascii - else if char == 'f' then - char = 0x0C.ascii - else if char == 'n' then - char = '\n' - else if char == 'r' then - char = '\r' - else if char == 't' then - char = '\t' - else if char == 'u' then - var code = text.substring(i + 1, 4).to_hex - # TODO UTF-16 escaping is not supported yet. - if code >= 128 then - char = '?' - else - char = code.ascii - end - i += 4 - end - # `"`, `/` or `\` => Keep `char` as-is. - end - res.add char - i += 1 - end - return res.write_to_string - end + private fun to_nit_string: String do return text.substring(1, text.length - 2).unescape_json.to_s end redef class Nvalue_object