Skip to content

Instantly share code, notes, and snippets.

@dotNetTree
Last active June 10, 2022 13:30
Show Gist options
  • Save dotNetTree/6d390aa11d6c7931a9f32cc8796b68d8 to your computer and use it in GitHub Desktop.
Save dotNetTree/6d390aa11d6c7931a9f32cc8796b68d8 to your computer and use it in GitHub Desktop.
Code Spitz 90 - 코틀린 언어편 (2) 과제
// 1. Set도 동작하게
// 2. Map도 동작하게
// 3. inline 함수에 대해 조사한 뒤 최대 반영
// 4. 어떤 클래스에 toJSON():String 메소드가 있다면 그 메소드를 통해 stringify를 하게 하시오
@Target(AnnotationTarget.PROPERTY)
annotation class Ex // 제외할 속성
@Target(AnnotationTarget.PROPERTY)
annotation class Name(val name: String) // json용 별도 이름지정
fun stringify(value: Any): String = StringBuilder().run {
jsonValue(value)
toString()
}
fun StringBuilder.comma() { append(",") }
inline fun StringBuilder.wrap(begin: Char, end: Char, block: StringBuilder.() -> Unit) {
append(begin)
block()
append(end)
}
fun StringBuilder.jsonValue(value: Any?) {
when(value) {
null -> append("null")
is String -> jsonString(value)
is Boolean, is Number -> append("$value")
is Map<*, *> -> jsonMap(value)
is Array<*> -> jsonArray(value)
is Collection<*> -> jsonCollection(value)
else -> {
value::class.members.find { it.name == "toJSON" }.let {
if (it != null) append(it.call(value) as String)
else jsonObject(value)
}
}
}
}
private fun StringBuilder.jsonArray(target: Array<*>) {
wrap('[', ']') {
target.joinTo(this::comma) {
jsonValue(it)
}
}
}
private fun StringBuilder.jsonCollection(target: Collection<*>) {
wrap('[', ']') {
target.joinTo(this::comma) {
jsonValue(it)
}
}
}
private fun StringBuilder.jsonMap(target: Map<*, *>) {
wrap('{', '}') {
target.joinTo(this::comma) { k, v ->
jsonValue(k)
append(":")
jsonValue(v)
}
}
}
private fun StringBuilder.jsonString(v: String) = append(""""${v.replace("\"", "\\\"")}"""")
private fun <T: Any> StringBuilder.jsonObject(target: T) {
wrap('{', '}') {
target::class.members
.filterIsInstance<KProperty<*>>()
.filter { it.findAnnotation<Ex>() == null }
.joinTo(this::comma) {
jsonValue(it.findAnnotation<Name>()?.name ?: it.name)
append(":")
jsonValue(it.getter.call(target))
}
}
}
inline fun <T> Array<T>.joinTo(sep:() -> Unit, transform: (T) -> Unit) {
forEachIndexed { idx, el ->
if (idx != 0) sep()
transform(el)
}
}
inline fun <T> Iterable<T>.joinTo(sep:() -> Unit, transform: (T) -> Unit) {
forEachIndexed { idx, el ->
if (idx != 0) sep()
transform(el)
}
}
inline fun <K, T> Map<K, T>.joinTo(sep:() -> Unit, transform: (K, T) -> Unit) {
entries.forEachIndexed { idx, el ->
if (idx != 0) sep()
val (key, value) = el
transform(key, value)
}
}
class Json1(
val a: Int,
val b: String,
val c: List<Int>,
val d: Set<String>,
val e: Map<Any, Any>,
val f: Array<String>) { }
class Json2(@Ex val a: Int, @Name("title") val b: String) {}
class Json3(val e: Map<Any, Any>) {
fun toJSON(): String = """{"name":"kim"} """
}
class Json4() {
fun toJSON(): String = """{"a":3}"""
}
fun main(args: Array<String>) {
println("ready")
println(
stringify(Json1(
a = 3,
b = "abc",
c = arrayListOf(1,2,3),
d = setOf("kkk", "aaa"),
e = mapOf(
"age1" to 10,
"age2" to 20,
"props" to mapOf(
"name" to "kang",
"age" to 42,
"skills" to listOf("swift", "js", "ts", "kotlin", 1)
)
),
f = arrayOf("array", "test")
))
)
println(stringify(Json2(a = 10, b = "abc")))
println(
stringify(Json3(
e = mapOf(
"skills" to listOf("swift", "js", "ts", "kotlin", 1),
"props" to mapOf(
"name" to "kang",
"age" to 42,
"skills" to listOf("swift", "js", "ts", "kotlin", 1)
)
)
))
)
println(stringify(Json4()))
}
/* 출력
ready
{"a":3,"b":"abc","c":[1,2,3],"d":["kkk","aaa"],"e":{"age1":10,"age2":20,"props":{"name":"kang","age":42,"skills":["swift","js","ts","kotlin",1]}},"f":["array","test"]}
{"title":"abc"}
{"name":"kim"}
{"a":3}
*/
@hikaMaeng
Copy link

  1. set뿐아니라 배열도 포함하라고 했는데 Collection으로는 안됨
  2. 게다가 map도 Collection이라서 Collection 다음에 나오면 안됨
  3. toJSON을 companion에서 살필 필요는 없음. 짜피 this의 속성들을 이용할 가능성의 거의 대부분이므로 companion에서 일괄된 함수로 동작하는게 의미가 없음
    수정하삼

@dotNetTree
Copy link
Author

수정하였습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment