Deserialise large integer and decimals from JSON automatically. (#10463)

- Sort large integer and decimal JSON deserialization.
- Change type to be Integer instead of BigInt for large integers.
- Add tests.
- Update Table viz.
- Preserve white space in JSON viz.
![image](https://github.com/enso-org/enso/assets/4699705/48c83616-c0ed-4cb4-862a-34cd4fff09aa)

![image](https://github.com/enso-org/enso/assets/4699705/5bae9ccd-1d0f-4b70-aea5-d4cebc3d9df8)
This commit is contained in:
James Dunkerley 2024-07-05 22:56:08 +01:00 committed by GitHub
parent c2c4b95116
commit d65371096b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 48 additions and 6 deletions

View File

@ -29,6 +29,7 @@ const primitive = computed(() => {
<style scoped>
.string {
color: darkgreen;
white-space: pre-wrap;
}
.number {
color: darkgreen;

View File

@ -229,7 +229,7 @@ const numberFormat = new Intl.NumberFormat(undefined, {
function formatNumber(params: ICellRendererParams) {
const valueType = params.value?.type
let value
if (valueType === 'BigInt') {
if (valueType === 'Integer') {
value = BigInt(params.value?.value)
} else if (valueType === 'Decimal') {
value = Number(params.value?.value)

View File

@ -999,6 +999,12 @@ type Decimal
scale : Integer
scale self = self.big_decimal.scale
## PRIVATE
with_scale : Integer -> Decimal
private with_scale self new_scale:Integer =
if self.scale == new_scale then self else
Decimal.Value (self.big_decimal.setScale new_scale)
## PRIVATE
unscaled_value : Integer
unscaled_value self = self.big_decimal.unscaledValue

View File

@ -1,6 +1,7 @@
import project.Any.Any
import project.Data.Array.Array
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Decimal.Decimal
import project.Data.Map.Map
import project.Data.Numbers.Float
import project.Data.Numbers.Integer
@ -413,9 +414,12 @@ make_enso object =
js_object : JS_Object ->
## Handle deserializing date and time types.
type_name = js_object.get "type"
parsed = if type_name == "Date" then Date.from js_object else
if type_name == "Date_Time" then Date_Time.from js_object else
if type_name == "Time_Of_Day" then Time_Of_Day.from js_object else
js_object
parsed = case type_name of
"Date" -> Date.from js_object
"Date_Time" -> Date_Time.from js_object
"Time_Of_Day" -> Time_Of_Day.from js_object
"Decimal" -> Decimal.from js_object
"Integer" -> Integer.from js_object
_ -> js_object
if parsed.is_error then js_object else parsed

View File

@ -2,6 +2,7 @@ import project.Any.Any
import project.Data.Array.Array
import project.Data.Array_Proxy.Array_Proxy
import project.Data.Decimal.Decimal
import project.Data.Numeric.Math_Context.Math_Context
import project.Data.Json.JS_Object
import project.Data.Json.Json
import project.Data.Locale.Locale
@ -13,6 +14,7 @@ import project.Data.Text.Text
import project.Data.Text.Text_Sub_Range.Text_Sub_Range
import project.Data.Vector.Vector
import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.Deprecated.Deprecated
import project.Meta
import project.Nothing.Nothing
@ -57,15 +59,32 @@ Number.to_js_object self = case self of
## JS Safe Integer range -(2^53 - 1) to (2^53 - 1)
js_max_integer = 9007199254740991
if self >= -js_max_integer && self < js_max_integer then self else
JS_Object.from_pairs [["type", "BigInt"], ["value", self.to_text]]
JS_Object.from_pairs [["type", "Integer"], ["value", self.to_text]]
_ -> self
## PRIVATE
Converts a JS_Object to an Integer.
Integer.from (that:JS_Object) =
case that.get "type" == "Integer" && ["value"].all that.contains_key of
True -> Integer.parse (that.at "value")
False -> Error.throw (Illegal_Argument.Error "Invalid JS_Object for Integer.")
## PRIVATE
Converts the given value to a JSON serializable object.
Decimal.to_js_object : JS_Object
Decimal.to_js_object self =
JS_Object.from_pairs [["type", "Decimal"], ["value", self.to_text], ["scale", self.scale], ["precision", self.precision]]
## PRIVATE
Converts a JS_Object to a Decimal.
Decimal.from (that:JS_Object) =
case that.get "type" == "Decimal" && ["value", "scale", "precision"].all that.contains_key of
True ->
math_context = Math_Context.new (that.at "precision")
raw_value = Decimal.from_string (that.at "value") math_context
raw_value.with_scale (that.at "scale")
False -> Error.throw (Illegal_Argument.Error "Invalid JS_Object for Decimal.")
## PRIVATE
Converts the given value to a JSON serializable object.
For Nothing, booleans, numbers and strings, this is the value itself.

View File

@ -153,6 +153,18 @@ add_specs suite_builder =
Number.positive_infinity.to_json . should_equal "null"
Number.negative_infinity.to_json . should_equal "null"
group_builder.specify "should serialise large integers and parse back" <|
large_number = 1234567890123456789012345678901234567890
large_number.to_json . should_equal '{"type":"Integer","value":"1234567890123456789012345678901234567890"}'
large_number.to_json . parse_json . should_equal large_number
group_builder.specify "should serialise decimals and parse back" <|
decimal = Decimal.from_string "1234567890123456789012345678901234567890.1234567890123456789012345678901234567890"
decimal.to_json . should_equal '{"type":"Decimal","value":"1234567890123456789012345678901234567890.1234567890123456789012345678901234567890","scale":40,"precision":80}'
decimal.to_json . parse_json . should_equal decimal
decimal.to_json . parse_json . scale . should_equal 40
decimal.to_json . parse_json . precision . should_equal 80
suite_builder.group "JS_Object" group_builder->
group_builder.specify "should be buildable from pairs" <|
JS_Object.from_pairs [["foo", "bar"]] . to_json . should_equal '{"foo":"bar"}'