Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compiler): support json to struct conversion #3648

Merged
merged 12 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions docs/docs/03-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,12 @@ str.fromJson(jsonNumber); // RUNTIME ERROR: unable to parse number `123` as
num.fromJson(Json "\"hello\""); // RUNTIME ERROR: unable to parse string "hello" as a number
```

For each `fromJson()`, there is a `tryFromJson()` method which returns an optional `T?` which
indicates if parsing was successful or not:
```js
let s = str.tryFromJson(myJson) ?? "invalid string";
``````

##### 1.1.4.6 Mutability

To define a mutable JSON container, use the `MutJson` type:
Expand Down Expand Up @@ -335,7 +341,31 @@ Json.delete(immutObj, "hello");
// ^^^^^^^^^ expected `JsonMut`
```

##### 1.1.4.7 Serialization
##### 1.1.4.7 Assignment to user-defined structs
All [structs](#31-structs) also have a `fromJson()` method that can be used to parse `Json` into a
struct:
```js
struct Contact {
first: str;
last: str;
phone: str?;
}

let j = Json { first: "Wing", last: "Lyly" };
let myContact = Contact.fromJson(j);
assert(myContact.first == "Wing");
```
When a `Json` is parsed into a struct, the schema will be validated to ensure the result is
type-safe:
```js
let p = Json { first: "Wing", phone: 1234 };
Contact.fromJson(p);
// RUNTIME ERROR: unable to parse Contact:
// - field "last" is required and missing
// - field "phone" is expected to be a string, got number.
```

##### 1.1.4.8 Serialization

The `Json.stringify(j: Json): str` static method can be used to serialize a `Json` as a string
([JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)):
Expand All @@ -360,7 +390,7 @@ let boom = num.fromJson(j.get("boom"));
let o = Json.tryParse("xxx") ?? Json [1,2,3];
```

##### 1.1.4.8 Logging
##### 1.1.4.9 Logging

A `Json` value can be logged using `log()`, in which case it will be pretty-formatted:

Expand All @@ -378,13 +408,12 @@ my object is: {
}
```

#### 1.1.4.9 Roadmap
#### 1.1.4.10 Roadmap

The following features are not yet implemented, but we are planning to add them in the future:

* Array/Set/Map.fromJson() - see https://github.com/winglang/wing/issues/1796 to track.
* Json.entries() - see https://github.com/winglang/wing/issues/3142 to track.
* Schema validation and assignment to struct - see https://github.com/winglang/wing/issues/3139 to track.
* Equality, diff and patch - see https://github.com/winglang/wing/issues/3140 to track.

[`▲ top`][top]
Expand Down
44 changes: 44 additions & 0 deletions docs/docs/04-standard-library/02-std/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2068,6 +2068,50 @@ The length of the string.
---


### Struct <a name="Struct" id="@winglang/sdk.std.Struct"></a>

Shared behavior for all structs.


#### Static Functions <a name="Static Functions" id="Static Functions"></a>

| **Name** | **Description** |
| --- | --- |
| <code><a href="#@winglang/sdk.std.Struct.fromJson">fromJson</a></code> | Converts a Json to a Struct. |
| <code><a href="#@winglang/sdk.std.Struct.tryFromJson">tryFromJson</a></code> | Converts a Json to a Struct, returning nil if the Json is not valid. |

---

##### `fromJson` <a name="fromJson" id="@winglang/sdk.std.Struct.fromJson"></a>

```wing
Struct.fromJson(json: Json);
```

Converts a Json to a Struct.

###### `json`<sup>Required</sup> <a name="json" id="@winglang/sdk.std.Struct.fromJson.parameter.json"></a>

- *Type:* <a href="#@winglang/sdk.std.Json">Json</a>

---

##### `tryFromJson` <a name="tryFromJson" id="@winglang/sdk.std.Struct.tryFromJson"></a>

```wing
Struct.tryFromJson(json: Json);
```

Converts a Json to a Struct, returning nil if the Json is not valid.

###### `json`<sup>Required</sup> <a name="json" id="@winglang/sdk.std.Struct.tryFromJson.parameter.json"></a>

- *Type:* <a href="#@winglang/sdk.std.Json">Json</a>

---



## Structs <a name="Structs" id="Structs"></a>

### DatetimeComponents <a name="DatetimeComponents" id="@winglang/sdk.std.DatetimeComponents"></a>
Expand Down
13 changes: 13 additions & 0 deletions examples/tests/error/struct_from_json_1.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Note that this test has to be alone because it needs to compile successfully and fail at preflight.
// If it is run with other tests, subsequent failures will be ignored in snapshot.

struct Person {
name: str;
age: num;
}

let j = {name: "cool", age: "not a number"};

Person.fromJson(j);
// ^ ERROR: unable to parse Person:
// - instance.age is not of a type(s) number
25 changes: 25 additions & 0 deletions examples/tests/error/struct_from_json_2.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Note that this test has to be alone because it needs to compile successfully and fail at preflight.
// If it is run with other tests, subsequent failures will be ignored in snapshot.

struct Person {
name: str;
age: num;
}

struct Advisor extends Person {
id: str;
}

struct Student extends Person {
advisor: Advisor;
}

let missingAdvisor = {
name: "cool",
age: "not a number"
};

Student.fromJson(missingAdvisor);
// ^ ERROR: unable to parse Student:
// - instance.age is not of a type(s) number
// - instance requires property "advisor"
29 changes: 29 additions & 0 deletions examples/tests/error/struct_from_json_3.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Note that this test has to be alone because it needs to compile successfully and fail at preflight.
// If it is run with other tests, subsequent failures will be ignored in snapshot.

struct Person {
name: str;
age: num;
}

struct Advisor extends Person {
id: str;
}

struct Student extends Person {
advisors: Array<Advisor>;
}

let invalidAdvisorInArray = {
name: "cool",
age: "not a number",
advisors: [
{id: "advisor1", name: "Bob", age: 34},
{id: 10, name: "Jacob", age: 45}
]
};

Student.fromJson(invalidAdvisorInArray);
// ^ ERROR: unable to parse Student:
// - instance.age is not of a type(s) number
// - instance.advisors[1].id is not of a type(s) string
29 changes: 29 additions & 0 deletions examples/tests/error/struct_from_json_4.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Note that this test has to be alone because it needs to compile successfully and fail at preflight.
// If it is run with other tests, subsequent failures will be ignored in snapshot.

struct Person {
name: str;
age: num;
}

struct Advisor extends Person {
id: str;
}

struct Student extends Person {
advisors: Set<Advisor>; // <== Using Set instead of Array
}

// Try adding two of the same adivsor
let invalidAdvisorInArray = {
name: "cool",
age: 22,
advisors: [
{id: "advisor1", name: "Bob", age: 34},
{id: "advisor1", name: "Bob", age: 34},
]
};

Student.fromJson(invalidAdvisorInArray);
// ^ ERROR: unable to parse Student:
// - instance.advisors contains duplicate item
hasanaburayyan marked this conversation as resolved.
Show resolved Hide resolved
18 changes: 18 additions & 0 deletions examples/tests/error/struct_from_json_5.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Note that this test has to be alone because it needs to compile successfully and fail at preflight.
// If it is run with other tests, subsequent failures will be ignored in snapshot.

struct Foo {
names: Map<str>;
}

let jFoo = {
names: {
a: "Amanda",
b: "Barry",
c: 10
}
};

Foo.fromJson(jFoo);
// ^ ERROR: unable to parse Foo:
// - instance.names.c is not of a type(s) string
23 changes: 23 additions & 0 deletions examples/tests/invalid/struct_json_conversion.w
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
bring cloud;

struct A {
a: str;
b: cloud.Bucket;
}

A.fromJson({});
//^^^^^^^^ Struct "A" contains field "b" which cannot be represented in Json

struct B {
a: A;
}

B.fromJson({});
//^^^^^^^^ Struct "B" contains field "a" which cannot be represented in Json

struct C extends A {
c: num;
}

C.fromJson({});
//^^^^^^^^ Struct "C" contains field "b" which cannot be represented in Json
Loading
Loading