How to Convert JSON to CSV in Kotlin
How to Convert JSON to CSV in Kotlin
Converting JSON data to CSV is a common requirement in many applications, especially when working with data exchange or reporting. In this article, we will explore how to achieve this in Kotlin, a modern and concise programming language. We will provide a step-by-step guide, covering the most common use case, edge cases, and performance tips.
Quick Example
Here is a minimal example that converts a JSON string to a CSV string:
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
fun jsonToCsv(jsonString: String): String {
val gson = Gson()
val listType = object : TypeToken<List<Map<String, String>>>() {}.type
val dataList = gson.fromJson(jsonString, listType)
return dataList.joinToString("\n") { row ->
row.values.joinToString(",")
}
}
You can use this function like this:
val jsonString = "[{\"name\":\"John\",\"age\":\"25\"},{\"name\":\"Alice\",\"age\":\"30\"}]"
val csvString = jsonToCsv(jsonString)
println(csvString) // Output: John,25\nAlice,30
Step-by-Step Breakdown
Let's break down the code:
- We import the necessary libraries:
com.google.gson.Gsonfor JSON parsing andcom.google.gson.reflect.TypeTokenfor type safety. - We define a function
jsonToCsvthat takes a JSON string as input and returns a CSV string. - We create a
Gsoninstance to parse the JSON string. - We define a
TypeTokento specify the type of data we expect: a list of maps with string keys and values. - We use
Gsonto parse the JSON string into a list of maps. - We use the
joinToStringfunction to concatenate the values of each map into a CSV string.
Handling Edge Cases
Empty/Null Input
If the input JSON string is empty or null, we should return an empty CSV string:
fun jsonToCsv(jsonString: String?): String {
if (jsonString.isNullOrEmpty()) return ""
// ...
}
Invalid Input
If the input JSON string is invalid, Gson will throw a JsonSyntaxException. We can catch this exception and return an error message:
fun jsonToCsv(jsonString: String): String {
try {
// ...
} catch (e: JsonSyntaxException) {
return "Invalid JSON input"
}
}
Large Input
If the input JSON string is very large, we may need to process it in chunks to avoid memory issues. We can use a JsonReader to read the JSON string chunk by chunk:
fun jsonToCsv(jsonString: String): String {
val jsonReader = JsonReader(StringReader(jsonString))
val csvBuilder = StringBuilder()
jsonReader.beginArray()
while (jsonReader.hasNext()) {
jsonReader.beginObject()
while (jsonReader.hasNext()) {
csvBuilder.append(jsonReader.nextName()).append(",")
csvBuilder.append(jsonReader.nextString()).append("\n")
}
jsonReader.endObject()
}
jsonReader.endArray()
return csvBuilder.toString()
}
Unicode/Special Characters
If the input JSON string contains Unicode or special characters, we need to ensure that they are properly encoded in the CSV output. We can use the StringEscapeUtils class from the Apache Commons Lang library to escape special characters:
import org.apache.commons.lang3.StringEscapeUtils
fun jsonToCsv(jsonString: String): String {
// ...
val csvString = dataList.joinToString("\n") { row ->
row.values.joinToString(",") { value ->
StringEscapeUtils.escapeCsv(value)
}
}
return csvString
}
Common Mistakes
Wrong TypeToken
Using a wrong TypeToken can lead to incorrect parsing of the JSON string:
// Wrong
val listType = object : TypeToken<List<String>>() {}.type
// Correct
val listType = object : TypeToken<List<Map<String, String>>>() {}.type
Missing Error Handling
Not handling errors can lead to unexpected behavior:
// Wrong
fun jsonToCsv(jsonString: String): String {
val gson = Gson()
val dataList = gson.fromJson(jsonString, listType)
return dataList.joinToString("\n") { row ->
row.values.joinToString(",")
}
}
// Correct
fun jsonToCsv(jsonString: String): String {
try {
// ...
} catch (e: JsonSyntaxException) {
return "Invalid JSON input"
}
}
Inefficient CSV Generation
Using the joinToString function can be inefficient for large datasets. Using a StringBuilder can be more efficient:
// Wrong
return dataList.joinToString("\n") { row ->
row.values.joinToString(",")
}
// Correct
val csvBuilder = StringBuilder()
dataList.forEach { row ->
csvBuilder.append(row.values.joinToString(",")).append("\n")
}
return csvBuilder.toString()
Performance Tips
- Use a
JsonReaderto read the JSON string chunk by chunk for large inputs. - Use a
StringBuilderto generate the CSV string instead of using thejoinToStringfunction. - Use the
StringEscapeUtilsclass to escape special characters in the CSV output.
FAQ
Q: What is the best way to handle invalid JSON input?
A: You can catch the JsonSyntaxException thrown by Gson and return an error message.
Q: How can I handle large JSON inputs?
A: You can use a JsonReader to read the JSON string chunk by chunk.
Q: How can I escape special characters in the CSV output?
A: You can use the StringEscapeUtils class from the Apache Commons Lang library.
Q: What is the most efficient way to generate the CSV string?
A: You can use a StringBuilder instead of the joinToString function.
Q: Can I use this code for other data formats?
A: No, this code is specifically designed for JSON to CSV conversion. You may need to modify it for other data formats.