You have already learned about the benefits of transferring data by serializing it into JSON format. Manually converting classes isn't very convenient, but fortunately, there are plenty of libraries available that simplify the process. One of the most popular and frequently used in Android is called Moshi.
Moshi's API is much smaller than other well-known libraries like Jackson and Gson. Gson has more than a thousand methods, while Moshi has only half that number. Moshi also weighs 2-3 times less (just over 100kB compared to Gson's 300kB).
Moshi is fast and requires less memory, making it a great option to choose. In this topic, you'll learn how to use it and find out more about the structure of JSON along the way.
- data 전송을 위해서 json 으로 변환할 때 moshi 라이브러리를 사용해보자. 이를 추천한다.
- 물론 라이브러리 안쓰고도 할 수 있지만 이건 편한 작업은 아니다.
- JSON 변환 라이브러리로 잘 알려져있는게 Jackson 과 Gson 인데 이것보다 Moshi 가 더 가볍다. 실제 메소드 숫자 뿐 아니라 메모리 사용량도. 그리고 더 빠르다.
To use the Moshi library, we need to add the following dependency to our build.gradle.kts file in the dependencies section:
implementation("com.squareup.moshi:moshi-kotlin:1.11.0")
With this in place, our IDE will import the required classes as we write our code.
You can find out more by visiting Moshi's GitHub page.
We'll start by defining a Human class that we can use to perform operations:
class Human(var name: String, var age: Int, var friends: Array<String>)Now let's create an instance of the Human class:
val human = Human("Mike", 20, arrayOf("Alex", "Valery", "Ann"))To work with Moshi, we need to use the Builder pattern to create the following object:
val moshi = Moshi.Builder() .add(KotlinJsonAdapterFactory()) .build()Next, we need to create an adapter to serialize the Human class and pass it the correct reference. This can be done with the Kotlin Classname::class.java feature:
val humanAdapter = moshi.adapter(Human::class.java)That's it! Now we're all set to quickly and conveniently convert our object into a JSON representation. For simplicity, we'll print the string to the output stream. However, in an actual application, we could save the result to a text file or database.
print(humanAdapter.toJson(human)) // {"name":"Mike","age":20,"friends":["Alex","Valery","Ann"]}
- Moshi 를 사용하는 방법에 대해서.
- Builder Pattern 을 통해서 Moshi 를 만들고.
- 어떤 클래스를 JSON 으로 변환할 건지 알려주기 위해서 Moshi Adapter 를 만들어주고
- 그 어댑터에 해당 객체를 넣어주면 변환이 된다.
We've got our adapter ready for the class, so let's try to use it to recreate a new object from a JSON string:
We've got our adapter ready for the class, so let's try to use it to recreate a new object from a JSON string:The fromJson method doesn't return a Human, it returns a Human? object. This is because it can be nullable (the fromJson method may not recognize the supplied JSON string), so ?. is used to access it safely.
Now we want to deserialize a more complex structure — a list of class objects.
Because our transformation will involve two classes, Human and List, we need to pass them in a ParameterizedType object. This approach allows us to store information about both classes together so it can be passed to the adapter for use during deserialization:
val humanList = listOf(human, newHuman) val type = Types.newParameterizedType(List::class.java, Human::class.java) val humanListAdapter = moshi.adapter<List<Human?>>(type) print(humanListAdapter.toJson(humanList)) // [{"name":"Mike","age":20,"friends":["Alex","Valery","Ann"]},{"name":"John","age":25,"friends":["Mike","Helen"]}]Having created a suitable adapter by combining the List and Human classes, we can use it to convert a JSON string:
val jsonStr = """[{"name":"Nick","age":10,"friends":["Valery"]}, {"name":"John","age":25,"friends":[]}, {"name":"Kate","age":40,"friends":[]}] """.trimIndent() val newHumanList = humanListAdapter.fromJson(jsonStr)
- Json 형식의 String 을 객체로 변환 시키는 방법에 대해서 알아보겠다. (Deserialize)
- String 은 trimIndent() 메소드 호출로 깔끔하게 만든다.
- 그 다음 fromJson() 을 통해서 객체로 변환할 수 있지만 JSON 형식의 문자열을 제대로 읽지 못해서 null 이 반환 될수도 있다.
- 그러므로 Safety Call 인 ?. 를 이용해서 접근하자.
- 추가로 List 형식의 클래스를 Json 형식으로 변환할려면 어떻게 해야핳까?
- 두 가지 타입이 필요하므로 Types.newParameterizedType(List::class.java, Human::class.java) 로 생성한 ParameterizedType 이 필요하다.
- 이를 어댑터에 넘기면 된다.
In the previous section, we learned how to retrieve objects from JSON via deserialization. Now we'll look at some examples of how to make use of them:
It's simple to find out the name of the newHuman object we created with our adapter:
print(newHuman?.name) // JohnWe can output more complex data as well. Let's see who John's friends are!
print(newHuman?.friends.contentToString()) // [Mike, Helen]We also created an adapter based on two classes that enabled us to deserialize a list of Human objects. Here's what it contains:
for (h in newHumanList!!) { print(h?.name + " ") } // Nick John KateWe are sure our list is definitely not null, so use !! when we refer to it.
For the final example, let's change our class a little by adding a Map that displays subject grades:
class Human(var name: String, var age: Int, var friends: Array<String>, var grades: Map<String, String>)Next, create an appropriate adapter that includes Map:
val type = Types.newParameterizedType(List::class.java, Human::class.java, Map::class.java) val humanListAdapter = moshi.adapter<List<Human?>>(type)We finish by converting our objects from a JSON string as we did before:
val jsonStr = """[{"name":"Ruslan","age":20,"friends":["Victoria","Valery"], "grades":{"Math":"A","Philosophy":"F","Physics":"D"}}, {"name":"Victoria","age":35,"friends":["Ruslan","Anastasia"], "grades":{"Math":"B","Philosophy":"C","Physics":"B"}}]""".trimIndent() val humanList = humanListAdapter.fromJson(jsonStr)But this time, we're also able to display information about people's grades, as shown below:
for (h in humanList!!) { println(h?.name + " has a grade of ${h!!.grades["Math"]} in maths") } // Ruslan has a grade of A in maths // Victoria has a grade of B in maths
- Deserialized 해서 사용하느 경우는 뭐 Safety call 을 이용해서 사용하면 되고
- 널이 아님을 보장할 수 있다면 !! 를 이용하면 되고
- 객체에 또 Map 과 같은 타입이 있는 경우, ParameterizedType 을 넣으면 된다.