Skip to content

Commit

Permalink
Deserialise large integer and decimals from JSON automatically. (#10463)
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
jdunkerley authored Jul 5, 2024
1 parent c2c4b95 commit d653710
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const primitive = computed(() => {
<style scoped>
.string {
color: darkgreen;
white-space: pre-wrap;
}
.number {
color: darkgreen;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 8 additions & 4 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Json.enso
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 12 additions & 0 deletions test/Base_Tests/src/Data/Json_Spec.enso
Original file line number Diff line number Diff line change
Expand Up @@ -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"}'
Expand Down

0 comments on commit d653710

Please sign in to comment.