Skip to content

Commit 6a19bdc

Browse files
committed
Review JSON tutorials
1 parent 3862453 commit 6a19bdc

File tree

4 files changed

+136
-103
lines changed

4 files changed

+136
-103
lines changed
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
UPickle is the library for working with JSONs in Scala Toolkit.
1+
uPickle is the JSON serialization library of the Scala Toolkit, it includes uJson, a Scala representation of the JSON format.
22

33
{% altDetails install-info-box 'Installing upickle' %}
44

55
## Installing upickle
66

7-
{% tabs upickle-install-methods %}
8-
{% tab 'Scala CLI' %}
9-
In Scala CLI, we can install the entire toolkit in a single line:
7+
{% tabs upickle-install-methods %}
8+
{% tab 'Scala CLI' %}
9+
Using Scala CLI, you can install the entire toolkit in a single line:
1010
```scala
1111
//> using toolkit
1212
```
1313

14-
Alternatively, we can install a specific version of upickle:
14+
Alternatively, you can install a specific version of upickle:
1515
```scala
1616
//> using lib "com.lihaoyi::upickle:1.6.0"
1717
```
18-
{% endtab %}
19-
{% tab 'sbt' %}
20-
In our build.sbt file, we add the dependency to the upickle library:
18+
{% endtab %}
19+
{% tab 'sbt' %}
20+
In your build.sbt file, you can add the dependency to the upickle library:
2121
```scala
2222
lazy val example = project.in(file("example"))
2323
.settings(
2424
scalaVersion := "3.2.1",
2525
libraryDependencies += "com.lihaoyi" %% "upickle" % "1.6.0"
2626
)
2727
```
28-
{% endtab %}
29-
{% tab 'Mill' %}
30-
In your build.sc file, we add the artifact to `ivyDeps`:
28+
{% endtab %}
29+
{% tab 'Mill' %}
30+
In your build.sc file, you can add the dependency to the upickle library:
3131
```scala
3232
object example extends ScalaModule {
3333
def scalaVersion = "3.2.1"
@@ -37,6 +37,6 @@ object example extends ScalaModule {
3737
)
3838
}
3939
```
40-
{% endtab %}
41-
{% endtabs %}
40+
{% endtab %}
41+
{% endtabs %}
4242
{% endaltDetails %}

_overviews/toolkit/upickle-modify-jsons.md

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,59 +9,59 @@ next-page:
99

1010
{% include markdown.html path="_markdown/install-upickle.md" %}
1111

12-
## Modifying jsons
13-
If you want to set, modify or remove fields of a json, you can do it with Scala Toolkit.
14-
First, you have to load the json. You can either do it from a json loaded to a `String`,
15-
or operate on json generated from Scala values.
16-
17-
## Loading a json text and modyfing it
18-
To read the `json` from text, you can use the `ujson.read` function.
19-
This function returns an object representing the json that you can operate on.
20-
Adding new fields and updating their values is done as a simple assignment:
12+
The `ujson.read` method creates a mutable representation of JSON that you can update, by adding, modifying or removing any field and element of its JSON objects and arrays.
13+
First you need to read the JSON string, then you can update it, and finally you can write it back to a String.
14+
2115
```scala
16+
// Read the JSON string
17+
val json = ujson.read("""{"name":"John","pets":["Toolkitty","Scaniel"]}""")
18+
19+
// Update it
2220
json("name") = "Peter"
21+
json("surname") = "Scalinsky"
22+
json("pets").arr.remove(1)
23+
24+
// Write it back to a String
25+
val result: String = ujson.write(json)
26+
println(result)
27+
// Prints {"name":"Peter","pets":["Toolkitty"],"surname":"Scalinisky"}
2328
```
24-
The code above would set the `name` field of the object in `json` to `Peter`.
25-
If the field is present, then it will be updated. If not, a new one is created.
26-
Below, you can see an example of all these operations put together.
27-
The `ujson.write` function allowed us to convert back the json object represention to a `String`.
29+
30+
31+
## Modifying and adding fields
32+
33+
You can access the field `"name"` with `json("name")` and then assign it a new value with the `=` statement.
34+
If the field does not yet exist, it is added to the JSON object.
35+
2836
```scala
2937
val json = ujson.read("""{"name":"John","pets":["Toolkitty","Scaniel"]}""")
3038
json("name") = "Peter"
3139
json("surname") = "Scalinsky"
32-
val jsonString: String = ujson.write(json)
33-
println(jsonString)
34-
//prints "{"name":"Peter","pets":["Toolkitty","Scaniel"],"surname":"Scalinisky"}"
3540
```
3641

42+
In the above code example, we change the value of the field `"name"`, from `"John"` to `"Peter"`, and we add a new field `"surname"` with value `"Scalinsky"`.
43+
3744
## Removing fields
38-
To remove fields from json you need to declare whether you are removing them from an `Object` or an `Array`.
39-
- To remove a field from an object, you can use the `.obj.remove("name")` function on json object
40-
- To remove a field from an array, you can use the `.obj.remove(index)` function on json object
41-
Following the previous example, below you can see how to remove some fields from it.
45+
46+
To remove a field from a JSON object:
47+
- declare that the JSON value is an object by calling the `obj` method
48+
- call the `remove` method with the name of the field to remove.
49+
4250
```scala
4351
val json = ujson.read("""{"name":"John","pets":["Toolkitty","Scaniel"]}""")
44-
json.obj.remove("name") // remove "name" field
45-
json("pets").arr.remove(1) // remove pet with index 1 ("Scaniel")
46-
val jsonString: String = ujson.write(json)
47-
println(jsonString) // prints {"pets":["Toolkitty"]}
52+
json.obj.remove("name") // Remove "name" field from object
53+
println(ujson.write(json))
4854
```
49-
Above, we first removed the field `name` from the top-level object in the json.
50-
Then, we selected the array `pets` and removed the value with index `1` from it.
5155

52-
## Operating on jsons generated from Scala values
53-
If you want to operate on jsons generate from Scala values (like in the [How to write JSONs]({% link _overviews/toolkit/upickle-write-json.md %}) tutorial), then it is possible as well.
54-
Usually, `upickle.write` operation outputs a `String`. But, if you replace it with `upickle.writeJs`, then it returns a json represention from `ujson`.
55-
Then you can operate on it in the same way as you did in the previous code snippets in this tutorial. For example:
56-
```scala
57-
import upickle.default._
56+
## Removing elements from arrays
5857

59-
case class PetOwner(name: String, pets: List[String])
60-
given ReadWriter[PetOwner] = macroRW
61-
val petOwner = PetOwner("Peter", List("Toolkitty", "Scaniel"))
62-
val json = writeJs[PetOwner](petOwner)
63-
json("surname") = "Scalinsky"
64-
val jsonString = ujson.write(json)
65-
println(jsonString)
66-
//Prints {"name":"Peter","pets":["Toolkitty","Scaniel"],"surname":"Scalinsky"}
67-
```
58+
To remove an element from an JSON array:
59+
- declare that the JSON value is an array by calling the `arr` method
60+
- call the `remove` method with the index of the element to remove.
61+
62+
```scala
63+
val json = ujson.read("""{"name":"John","pets":["Toolkitty","Scaniel"]}""")
64+
json("pets").arr.remove(1) // Remove pet at index 1 ("Scaniel")
65+
val jsonString: String = ujson.write(json)
66+
println(jsonString) // Prints {"name":"John","pets":["Toolkitty"]}
67+
```
Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: How to read a JSON to typed structure?
2+
title: How to parse a JSON value to a data type?
33
type: section
44
description: How to read a JSON to typed structure with Scala Toolkit
55
num: 21
@@ -9,18 +9,18 @@ next-page: upickle-write-json
99

1010
{% include markdown.html path="_markdown/install-upickle.md" %}
1111

12-
In Scala Toolkit, it is possible to read a JSON and values inside of it in two ways:
13-
- First one offers everything you need to quickly extract data from a JSON, without requiring any specific structure.
14-
This approach is described in the [How to read a JSON]({% link _overviews/toolkit/upickle-read-json.md %}) tutorial. Visit it if you want a simple and fast way to read JSONs.
15-
- The second approach one allows you to work with the json in a fully typed code, and even to provide your custom data structures.
16-
This approach is described in this tutorial. It is more well-suited when you plan on reusing, storing and operating on the structures loaded from jsons.
17-
18-
## Reading JSONs to a typed structure
19-
To perform typed operations on JSONs, you can utilize a `upickle` Toolkit library.
20-
This approach has an advantage of additional safety and convenience of working with the structured data.
21-
As an example - if you know that your JSON contains a set of fields, where each field's value is an array of numbers,
22-
you can use the `read[Map[String, List[Int]]](json)` function that will return a map of this exact type.
23-
You can see an example of that below
12+
In the Scala Toolkit, there are two ways of reading JSON: the dynamic way and the fully-typed way.
13+
- In the first approach, we can quickly extract data from JSON, without requiring any specific schema.
14+
To discover this approach read the [How to read JSON dynamically]({% link _overviews/toolkit/upickle-read-json.md %}). It is simple and fast.
15+
- In the second approach, you define your own data type, and transform JSON objects into instances of that data type, making sure that all the required fields are defined with valid types.
16+
This approach is described below.
17+
It is well-suited when you need to validate the JSON values, to store them or to operate on them in a safe way.
18+
19+
## Parsing a JSON object to a Map
20+
To type a JSON object into a Scala `Map`, you can use the `upickle` library.
21+
This approach is safe and convenient for working with structured data.
22+
It allows you to quiclky validate the structure of the JSON string.
23+
2424
```scala
2525
import upickle.default._
2626
val jsonString = """{"primes": [2, 3, 5], "evens": [2, 4, 6]} """
@@ -29,39 +29,64 @@ val primes = map("primes")
2929
println(primes.head) // Prints 2
3030
```
3131

32-
### Reading JSONs of your own data types
33-
In Scala, you can use a `case class` to define your own data type. For example, if you wanted to represent a Person with names of its pets, you could do it as follows:
32+
In this example, we expect the JSON string to contain an object, where each fields's value is an array of numbers.
33+
We call the `upickle.default.read` with the type parameter `Map[String, List[Int]]` to transform the JSON object into a map of this exact type.
34+
35+
If the JSON value does not match the expected type, upickle throws an exception.
36+
37+
### Parsing a JSON object to a custom data type
38+
39+
In Scala, you can use a `case class` to define your own data type.
40+
For example, to represent the pets and their owner, you can do it as follows:
3441
```scala
3542
case class PetOwner(name: String, pets: List[String])
3643
```
37-
After defining this `case class`, you can read a JSON containing its fields. But first, you need to provide an instance of `ReadWriter` that will tell the library
38-
how to handle this type. Luckily, `upickle` is able to fully automate that and all have to do is:
44+
45+
To be able to read a `PetOwner` from a JSON we need to provde a given instance of `ReadWriter[PetOwner]`.
46+
Luckily, `upickle` is able to fully automate that and all you need to write is:
47+
3948
```scala
4049
given ReadWriter[PetOwner] = macroRW
4150
```
42-
`given` keyword may appear strange at first, but it just says that this value may be used later transparently in your code by some functions that needs a `ReadWriter[PetOwner]`.
43-
You don't need to think about it for too long, that's the only thing you need to do - as long as this value is available, you will be able to read a JSON that conforms to the type of a `PetOwner`.
44-
The second part of this definition, `macroRW`, automates everything that needs to be done to provide this mechanism capable of reading these JSONs.
45-
After this declaration, you can put everything together and read a JSON as you inteded to:
51+
52+
The `given` keyword may appear strange at first but is very powerful.
53+
Having a `given ReadWriter[PetOwner]` in the current scope allows you to ask the `upickle.default.read` method to return a value of `PetOwner`, like this `read[PetOwner](jsonString)`.
54+
As long as the given value is available, you will be able to read JSON that conforms to the type of a `PetOwner`.
55+
The second part of this definition, `macroRW`, automates the instanciation of `ReadWriter` so that we don't have to do it by hand.
56+
57+
After this declaration, you can put everything together and read a `PetOwner` from JSON:
4658
```scala
4759
import upickle.default._
4860

4961
case class PetOwner(name: String, pets: List[String])
62+
5063
given ReadWriter[PetOwner] = macroRW
64+
5165
val jsonString = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
5266
val petOwner: PetOwner = read[PetOwner](jsonString)
53-
val ownerName = petOwner.name
54-
val ownerFirstAnimal = petOwner.pets.head
55-
println(s"$ownerName has a pet called $ownerFirstAnimal")
56-
// Prints "Peter has a pet called Toolkitty
57-
```
67+
val firstPet = petOwner.pets.head
68+
println(s"${petOwner.name} has a pet called $firstPet")
69+
// Prints "Peter has a pet called Toolkitty"
70+
```
71+
72+
### Making the given ReadWriter globally available
5873

59-
### Providing ReadWriter in the companion object
74+
The given `ReadWriter[PetOwner]` is only available in the scope in which it is defined.
75+
To make it available globally you can define it in the companion object of `PetOwner`.
6076

61-
If you want to have the `ReadWriter[PetOwner]` always available with the `PetOwner`, no matter where in your project you are, you can use a Scala's feature called `companion objects`.
62-
Next to the `PetOwner` case class, you need to put an `object` that is called the same - `object PetOwner`. In that object, you can put the `given` ReadWriter.
63-
After you do that, the `ReadWriter` will be always available alongside the `PetOwner` case class.
6477
```scala
78+
case class PetOwner(name: String, pets: List[String])
79+
6580
object PetOwner:
66-
given ReadWriter[PetOwner] = macroRW
81+
given ReadWriter[PetOwner] = macroRW
82+
```
83+
84+
The given `ReadWriter[PetOwner]` is now available globally.
85+
That means you can read a `PetOwner` from a JSON object from any file, class, or method.
86+
87+
```scala
88+
import upickle.default.read
89+
90+
def readPetOwnerFromJson(json: String): PetOwner =
91+
read[PetOwner](json)
6792
```
Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,49 @@
11
---
2-
title: How to read a JSON?
2+
title: How to read JSON dynamically?
33
type: section
4-
description: How to read a JSON with Scala Toolkit.
4+
description: How to read JSON with Scala Toolkit.
55
num: 20
66
previous-page: upickle-intro
77
next-page: upickle-read-json-typed
88
---
99

1010
{% include markdown.html path="_markdown/install-upickle.md" %}
1111

12-
In Scala Toolkit, it is possible to read a JSON and values inside of it in two ways:
13-
- First one offers everything you need to quickly extract data from a JSON, without requiring any specific structure. This approach is described in this article. It is a simple and fast way to read JSONs.
14-
- While the second approach allows you to work with the json in a fully typed code, and even to provide your custom data structures. To discover this approach read the [How to read a JSON to typed structure]({% link _overviews/toolkit/upickle-read-json-typed.md %}) tutorial. It is more well-suited when you plan on reusing, storing and operating on the structures loaded from jsons.
12+
In the Scala Toolkit, there are two ways of reading JSON: the dynamic way and the fully-typed way.
13+
- In the first approach, we can quickly extract data from JSON, without requiring any specific schema.
14+
This approach is described below.
15+
It is simple and fast.
16+
- In the second approach, you define your own data type, to parse JSON objects into instances of that data type, making sure that all the required fields are defined with the expected types.
17+
To discover this approach read the [How to parse a JSON value to a data type]({% link _overviews/toolkit/upickle-read-json-typed.md %}).
18+
It is well-suited when you need to validate the JSON values, to store them or to operate on them in a safe way.
1519

16-
## Reading JSONs dynamically
17-
If you want to just parse a JSON and access some of its field, you may just use a `ujson` library that is included with Toolkit.
18-
Function `ujson.read` allows you to read a JSON and access it afterwards.
19-
You can access the fields in the returned JSON object by just providing their names exactly as you would to a standard function.
20-
Afterwards you have to declare what you expect to be inside the field and extract it, for example `str` extracts field's value as a `String`.
21-
For example, code below prints out `Peter`.
20+
## Reading JSON dynamically
21+
If you want to parse a JSON object and access some of its field, you can use the uJson library, which is brought by uPickle.
22+
23+
The method `ujson.read` can parse a JSON string and make all of its fields available.
2224
```scala
2325
val jsonString = """{"name": "Peter", "age": 13}"""
2426
val json = ujson.read(jsonString)
2527
println(json("name").str) // Prints out "Peter"
2628
```
27-
You can operate on arrays similarly, accessing the indices as in the example below.
29+
30+
You can access a field by passing its name to the `json` value, and then specifying which type you expect.
31+
If `name` is a field that should contain a string, you can access it using `json("name").str`.
32+
33+
Similarly, you can access an element of an array by passing its indice, as in the example below:
2834
```scala
2935
val jsonString = """{"name": "Peter", "pets": ["Toolkitty", "Scaniel"]}"""
3036
val json = ujson.read(jsonString)
3137
val ownerName = json("name").str
32-
val firstPet = json("pets")(0) .str
38+
val firstPet = json("pets")(0).str
3339
println(s"$ownerName has a pet called $firstPet")
3440
```
35-
You can traverse the JSON structure as deeply as you want, to extract the fields you require.
41+
You can traverse the JSON structure as deeply as you want, to extract any nested field.
3642

3743
## Extracting values
38-
In the previous example we used the `.str` function on fields to extract a `String` from the field's value.
39-
Similiar operations are available to extract other types of values. Namely:
40-
- For the `Int`, you can use `.num` function
41-
- For the `Boolean`, you can use `.bool` function
44+
In the previous example we used the `str` method on fields to extract a `String` from the field's value.
45+
Similar operations are available to extract other types of values. Namely:
46+
- `num` for numeric values, it returns an `Int`
47+
- `bool` for boolean values, it returns a `Boolean`
48+
- `arr` for arrays
49+
- `obj` for objects

0 commit comments

Comments
 (0)