├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── common ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ ├── JSArray.kt │ │ ├── JSONDsl.kt │ │ ├── JSObject.kt │ │ ├── JSString.kt │ │ ├── collectionsH.kt │ │ ├── exceptions │ │ ├── JSException.kt │ │ └── JSSyntaxException.kt │ │ ├── internal │ │ ├── AbstractJSArray.kt │ │ ├── AbstractJSObject.kt │ │ ├── JSArrayImpl.kt │ │ ├── JSObjectImpl.kt │ │ ├── conversion.kt │ │ ├── exceptions.kt │ │ ├── internalH.kt │ │ ├── parsing.kt │ │ └── rendering.kt │ │ ├── json.kt │ │ ├── parsingH.kt │ │ └── renderingH.kt │ └── test │ └── kotlin │ └── me │ └── kgustave │ └── json │ └── test │ ├── InternalTests.kt │ ├── ParsingTests.kt │ └── TestName.kt ├── config ├── dokka.gradle ├── kotlin-common.gradle ├── kotlin-jvm.gradle └── kotlin2js.gradle ├── core ├── build.gradle ├── gradle.properties └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ ├── JSArray.kt │ │ ├── JSONDsl.kt │ │ ├── JSObject.kt │ │ ├── JSString.kt │ │ ├── JSWriter.kt │ │ ├── exceptions │ │ ├── JSException.kt │ │ └── JSSyntaxException.kt │ │ ├── internal │ │ ├── AbstractJSArray.kt │ │ ├── AbstractJSObject.kt │ │ ├── JSArrayImpl.kt │ │ ├── JSObjectImpl.kt │ │ ├── JSTokener.kt │ │ ├── JSWriterImpl.kt │ │ ├── conversion.kt │ │ ├── exceptions.kt │ │ ├── parsing.kt │ │ └── rendering.kt │ │ ├── io.kt │ │ ├── json.kt │ │ ├── options │ │ └── JSParsingOptions.kt │ │ ├── parsing.kt │ │ └── rendering.kt │ └── test │ ├── kotlin │ └── me │ │ └── kgustave │ │ └── json │ │ └── test │ │ ├── JsonTests.kt │ │ ├── ParsingTests.kt │ │ ├── constants.kt │ │ └── extension │ │ ├── SingleInstance.kt │ │ └── fileTest.kt │ └── resources │ ├── empty.txt │ └── test.json ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jdk6 ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ ├── IO.kt │ │ ├── JSON.kt │ │ ├── JSWriter.kt │ │ ├── internal │ │ ├── JSArrayImpl.kt │ │ ├── JSObjectImpl.kt │ │ ├── JSTokener.kt │ │ ├── JSWriterImpl.kt │ │ ├── conversion.kt │ │ ├── exceptions.kt │ │ └── rendering.kt │ │ └── options │ │ └── JSParsingOptions.kt │ └── test │ └── kotlin │ └── me │ └── kgustave │ └── json │ └── test │ ├── IOOperations.kt │ ├── JsonTests.kt │ └── TestName.kt ├── jdk8 ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ ├── IO.kt │ │ ├── JSON.kt │ │ ├── JSWriter.kt │ │ ├── internal │ │ ├── JSArrayImpl.kt │ │ ├── JSObjectImpl.kt │ │ ├── JSTokener.kt │ │ ├── JSWriterImpl.kt │ │ ├── conversion.kt │ │ ├── exceptions.kt │ │ └── rendering.kt │ │ └── options │ │ └── JSParsingOptions.kt │ └── test │ ├── kotlin │ └── me │ │ └── kgustave │ │ └── json │ │ └── test │ │ ├── IOOperations.kt │ │ ├── JsonTests.kt │ │ ├── TestName.kt │ │ ├── constants.kt │ │ └── extension │ │ ├── SingleInstance.kt │ │ └── fileTest.kt │ └── resources │ ├── empty.txt │ └── test.json ├── js ├── README.md ├── build.gradle ├── package-info.json └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ ├── JSON.kt │ │ └── internal │ │ ├── JSArrayImpl.kt │ │ ├── JSObjectImpl.kt │ │ ├── conversion.kt │ │ ├── exceptions.kt │ │ ├── hacks.kt │ │ └── rendering.kt │ └── test │ ├── kotlin │ └── me │ │ └── kgustave │ │ └── json │ │ └── test │ │ ├── IOTests.kt │ │ ├── TestName.kt │ │ └── mocha │ │ ├── api.kt │ │ └── dsl.kt │ └── resources │ ├── array.json │ └── object.json ├── libraries └── ktor │ ├── client │ ├── build.gradle │ └── src │ │ ├── main │ │ └── kotlin │ │ │ └── me │ │ │ └── kgustave │ │ │ └── json │ │ │ └── ktor │ │ │ └── client │ │ │ ├── JSKtorSerializer.kt │ │ │ └── extension.kt │ │ └── test │ │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ └── ktor │ │ └── client │ │ └── test │ │ ├── KotlinJsonKtorClientTests.kt │ │ └── extension │ │ └── engineProvider.kt │ ├── common │ ├── build.gradle │ └── src │ │ └── main │ │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ └── ktor │ │ ├── JSOutgoingContent.kt │ │ └── websocket │ │ ├── frames.kt │ │ └── sessions.kt │ └── server │ ├── build.gradle │ └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ └── ktor │ │ └── server │ │ ├── JSContentConverter.kt │ │ └── extension.kt │ └── test │ └── kotlin │ └── me │ └── kgustave │ └── json │ └── ktor │ └── server │ └── test │ └── serverTests.kt ├── reflect ├── build.gradle └── src │ ├── main │ └── kotlin │ │ └── me │ │ └── kgustave │ │ └── json │ │ └── reflect │ │ ├── JSConstructor.kt │ │ ├── JSDeserializer.kt │ │ ├── JSName.kt │ │ ├── JSOptional.kt │ │ ├── JSSerializable.kt │ │ ├── JSSerializer.kt │ │ ├── JSValue.kt │ │ ├── exceptions │ │ └── JSReflectionException.kt │ │ ├── experimental.kt │ │ ├── extension.kt │ │ └── internal │ │ ├── InstanceParameterMap.kt │ │ ├── NotAValue.kt │ │ ├── deserialization │ │ ├── DeserializationCache.kt │ │ ├── DeserializationStrategy.kt │ │ ├── DeserializationType.kt │ │ └── ParameterResolver.kt │ │ ├── exceptions.kt │ │ ├── hacks.kt │ │ └── serialization │ │ ├── PropertyHandler.kt │ │ ├── SerializationCache.kt │ │ ├── SerializationStrategy.kt │ │ └── convertValue.kt │ └── test │ └── kotlin │ └── me │ └── kgustave │ └── json │ └── test │ ├── Book.kt │ ├── House.kt │ ├── Person.kt │ └── ReflectionTests.kt └── settings.gradle /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [src/**] 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | trim_trailing_whitespace = true 11 | 12 | [*.{java, kt, kts, gradle}] 13 | insert_final_newline = true 14 | trim_trailing_whitespace = true 15 | indent_style = space 16 | end_of_line = lf 17 | indent_size = 4 18 | 19 | [*.{md}] 20 | insert_final_newline = false 21 | trim_trailing_whitespace = false 22 | indent_size = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij Project Files ### 2 | /.idea/ 3 | **/out/ 4 | 5 | ### NPM Files ### 6 | !package-info.json 7 | 8 | **/node_modules/ 9 | 10 | # We ignore any package.json 11 | # because they're all autogenerated 12 | package.json 13 | package-lock.json 14 | 15 | ### Gradle Project Files ### 16 | /.gradle/ 17 | **/build/ 18 | 19 | ### Other Files ### 20 | /keys/ -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinCommonConfig() -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/JSONDsl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * [DslMarker] for JSON closure Domain Specifying Language. 20 | * 21 | * @author Kaidan Gustave 22 | * @since 1.0 23 | */ 24 | @DslMarker 25 | annotation class JSONDsl -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/JSString.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * Interface used to stringify JSON style entities. 20 | * 21 | * The [toJsonString] provides a `indent` parameter for 22 | * pretty print JSON strings. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.0 26 | */ 27 | interface JSString { 28 | /** 29 | * Converts a value to a JSON entity as a [String] 30 | * rendered with the specified [indent] factor. 31 | */ 32 | fun toJsonString(indent: Int = 0): String 33 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/collectionsH.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * Creates a [JSObject] from the receiver [Map]. 20 | * 21 | * @receiver The [Map] to create a [JSObject] with. 22 | * 23 | * @return A [JSObject] created from the [Map]. 24 | */ 25 | expect fun Map.toJSObject(): JSObject 26 | 27 | /** 28 | * Creates a [JSArray] from the receiver [Collection]. 29 | * 30 | * @receiver The [Collection] to create a [JSArray] with. 31 | * 32 | * @return A [JSArray] created from the [Collection]. 33 | */ 34 | expect fun Collection<*>.toJSArray(): JSArray 35 | 36 | /** 37 | * Creates a [JSArray] from the receiver [List]. 38 | * 39 | * @receiver The [List] to create a [JSArray] with. 40 | * 41 | * @return A [JSArray] created from the [List]. 42 | */ 43 | expect fun List<*>.toJSArray(): JSArray 44 | 45 | /** 46 | * Creates a [JSArray] from the receiver [Array]. 47 | * 48 | * @receiver The [Array] to create a [JSArray] with. 49 | * 50 | * @return A [JSArray] created from the [Array]. 51 | */ 52 | expect fun Array<*>.toJSArray(): JSArray 53 | 54 | /** 55 | * Creates a [JSArray] from the receiver [Sequence]. 56 | * 57 | * @receiver The [Sequence] to create a [JSArray] with. 58 | * 59 | * @return A [JSArray] created from the [Sequence]. 60 | */ 61 | expect fun Sequence<*>.toJSArray(): JSArray 62 | -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/exceptions/JSException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.exceptions 17 | 18 | /** 19 | * Higher level exception for all other exceptions 20 | * that come from the kotlin-json library. 21 | * 22 | * This exception has two intentions: 23 | * 24 | * 1) To serve as a superclass and a catchall for all extending exceptions. 25 | * 2) To serve as a generic exception that does not fill the criteria for throwing a subclass of it. 26 | * 27 | * Generally speaking, unless otherwise documented, this should be used whenever 28 | * dealing with catching errors from various kotlin-json resources. 29 | * 30 | * @author Kaidan Gustave 31 | * @since 1.0 32 | */ 33 | open class JSException(message: String? = null, cause: Throwable? = null): RuntimeException(message, cause) -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/exceptions/JSSyntaxException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.exceptions 17 | 18 | /** 19 | * Sub-Exception of [JSException] raised whenever a parsing JSON string 20 | * does not meet a syntax requirement and fails to be valid JSON. 21 | * 22 | * @author Kaidan Gustave 23 | * @since 1.0 24 | */ 25 | class JSSyntaxException(message: String? = null, cause: Throwable? = null): JSException(message, cause) -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/AbstractJSObject.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("MemberVisibilityCanBePrivate") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.* 20 | 21 | /** 22 | * Abstract implementation of [JSObject]. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.0 26 | */ 27 | internal abstract class AbstractJSObject( 28 | protected val map: MutableMap = HashMap() 29 | ): JSObject, MutableMap by map { 30 | override fun put(key: String, value: Any?): Any? = map.put(key, convertValue(value)) 31 | override fun putAll(from: Map) { 32 | from.forEach { put(it.key, it.value) } 33 | } 34 | 35 | override fun isNull(key: String): Boolean { 36 | return map[key] === null 37 | } 38 | 39 | override fun toJsonString(indent: Int): String = buildJsonObjectString(indent) 40 | override fun toString(): String = toJsonString(0) 41 | } 42 | -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/JSArrayImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal expect class JSArrayImpl: AbstractJSArray { 22 | constructor() 23 | constructor(array: Array<*>) 24 | constructor(collection: Collection<*>) 25 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/JSObjectImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal expect class JSObjectImpl: AbstractJSObject { 22 | constructor() 23 | constructor(map: Map) 24 | constructor(vararg pairs: Pair) 25 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/conversion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import me.kgustave.json.JSArray 19 | import me.kgustave.json.JSObject 20 | 21 | internal fun convertString(value: Any?): String? { 22 | if(value is String) return value 23 | if(value is JSObject || value is JSArray) { 24 | // shouldn't return objects or arrays as strings! 25 | return null 26 | } 27 | return value.toString() 28 | } 29 | 30 | internal fun convertBoolean(value: Any?, fromString: Boolean = false): Boolean? { 31 | if(value is Boolean) return value 32 | if(fromString && (value is String && (value == "true" || value == "false"))) { 33 | // check if it is true or false so we don't 34 | //convert a non-boolean value and return 35 | //false when it should be null 36 | return value.toBoolean() 37 | } 38 | return null 39 | } 40 | 41 | internal fun convertLong(value: Any?, fromString: Boolean = false): Long? { 42 | (value as? Long ?: (value as? Int)?.toLong())?.let { return it } 43 | if(fromString && value is String) { 44 | return value.toLongOrNull() 45 | } 46 | return null 47 | } 48 | 49 | internal fun convertInt(value: Any?, fromString: Boolean = false): Int? { 50 | (value as? Int)?.let { return it } 51 | if(fromString && value is String) { 52 | return value.toIntOrNull() 53 | } 54 | return null 55 | } 56 | 57 | internal fun convertDouble(value: Any?, fromString: Boolean = false): Double? { 58 | (value as? Double ?: (value as? Float)?.toDouble())?.let { return it } 59 | if(fromString && value is String) { 60 | return value.toDoubleOrNull() 61 | } 62 | return null 63 | } 64 | 65 | internal fun convertFloat(value: Any?, fromString: Boolean = false): Float? { 66 | (value as? Float)?.let { return it } 67 | if(fromString && value is String) { 68 | return value.toFloatOrNull() 69 | } 70 | return null 71 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import me.kgustave.json.exceptions.JSException 19 | import kotlin.reflect.KClass 20 | 21 | internal inline fun checkJson(condition: Boolean, msg: () -> String) { 22 | if(!condition) { 23 | throw JSException(msg()) 24 | } 25 | } 26 | 27 | internal inline fun checkNotNullJson(value: T?, msg: () -> String): T { 28 | if(value === null) { 29 | throw JSException(msg()) 30 | } 31 | return value 32 | } 33 | 34 | internal inline fun tryWrap(block: () -> R): R { 35 | try { 36 | return block() 37 | } catch(t: Throwable) { 38 | throw JSException(cause = t) 39 | } 40 | } 41 | 42 | internal inline fun ignored(block: () -> Unit) = try { block() } catch(ignored: Throwable) {} -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/internalH.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import kotlin.reflect.KClass 19 | 20 | // Conversions 21 | 22 | internal expect fun convertValue(value: Any?): Any? 23 | 24 | // Exceptions 25 | 26 | internal expect fun isNotType(key: String, value: Any, type: KClass<*>): String 27 | 28 | internal expect fun isNotType(index: Int, value: Any, type: KClass<*>): String 29 | 30 | // Rendering 31 | 32 | internal expect fun renderValue(value: Any?, indent: Int, level: Int, newline: Boolean): String 33 | 34 | internal expect fun renderHexString(int: Int): String -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/internal/parsing.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | internal fun removeComments(str: String): String { 19 | // Instead of filtering out the commented lines, we simply skip them. 20 | // This doesn't save much if any overhead but considering the efficiency 21 | // we lose from comments in general, the quickest operation should be taken. 22 | return str.split('\n').joinToString("\n") { if(it.trim().startsWith("//")) "" else it } 23 | } 24 | 25 | internal fun stringToValue(str: String): Any? { 26 | if(str.isEmpty()) { 27 | return str 28 | } 29 | 30 | when(str) { 31 | "true" -> return true 32 | "false" -> return false 33 | "null" -> return null 34 | } 35 | 36 | val initial = str[0] 37 | if(initial in '0'..'9' || initial == '-') { 38 | ignored { 39 | if(str.indexOf('.') > -1 || str.indexOf('e') > -1 || str.indexOf('E') > -1 || str == "-0") { 40 | val d = str.toDouble() 41 | if(!d.isInfinite() && !d.isNaN()) { 42 | return d 43 | } 44 | } else { 45 | val long = str.toLong() 46 | if(str == long.toString()) { 47 | return if(long == long.toInt().toLong()) long.toInt() else long 48 | } 49 | } 50 | } 51 | } 52 | return str 53 | } 54 | -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/json.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("FunctionName", "NOTHING_TO_INLINE") 17 | package me.kgustave.json 18 | 19 | // Basic Access 20 | 21 | /** 22 | * Creates a [JSObject] using the provided key-value [pairs]. 23 | * 24 | * @param pairs The key-value [Pairs][Pair] to create a [JSObject] with. 25 | * 26 | * @return A new [JSObject]. 27 | */ 28 | expect fun jsObjectOf(vararg pairs: Pair): JSObject 29 | 30 | /** 31 | * Creates a [JSArray] using the provided [items]. 32 | * 33 | * @param items The items to create a [JSArray] with. 34 | * 35 | * @return A new [JSArray]. 36 | */ 37 | expect fun jsArrayOf(vararg items: Any?): JSArray 38 | 39 | // DSL 40 | 41 | /** 42 | * Creates a [JSObject] and runs the provided [block] on it. 43 | * 44 | * @param block The block to run. 45 | * 46 | * @return A new [JSObject]. 47 | */ 48 | @JSONDsl inline fun JSObject(block: JSObject.() -> Unit): JSObject = jsObjectOf().apply(block) 49 | 50 | /** 51 | * Creates a [JSArray] with the provided [items]. 52 | * 53 | * @param items The items to create the [JSArray] with. 54 | * 55 | * @return A new [JSArray]. 56 | */ 57 | @JSONDsl inline fun JSArray(vararg items: Any?): JSArray = jsArrayOf(*items) 58 | 59 | /** 60 | * Creates a [JSArray] of the specified [size], where each element 61 | * is calculated by calling the specified [init] function. 62 | * 63 | * The [init] function returns an array element given its index. 64 | * 65 | * @param size The size of the [JSArray]. 66 | * @param init A function to initialize the array where the value 67 | * is the index to place the calculated value at. 68 | * 69 | * @return A new [JSArray]. 70 | */ 71 | @JSONDsl inline fun JSArray(size: Int, init: (Int) -> Any?): JSArray { 72 | val array = jsArrayOf() 73 | for(i in 0 until size) { 74 | array[i] = init(i) 75 | } 76 | return array 77 | } -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/parsingH.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * Parses the provided JSON [String] into a [JSObject]. 20 | * 21 | * @param json The JSON [String] to parse. 22 | * 23 | * @return A [JSObject] parsed from the JSON [String]. 24 | * 25 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 26 | */ 27 | expect fun parseJsonObject(json: String): JSObject 28 | 29 | /** 30 | * Parses the provided JSON [String] into a [JSArray]. 31 | * 32 | * @param json The JSON [String] to parse. 33 | * 34 | * @return A [JSArray] parsed from the JSON [String]. 35 | * 36 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 37 | */ 38 | expect fun parseJsonArray(json: String): JSArray 39 | -------------------------------------------------------------------------------- /common/src/main/kotlin/me/kgustave/json/renderingH.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * Renders the receiver [Map] to a JSON Object string 20 | * with an optional [indent] factor. 21 | * 22 | * @param indent The indent factor, default 0. 23 | * 24 | * @return The rendered JSON Object string. 25 | */ 26 | expect fun Map.toJsonString(indent: Int = 0): String 27 | 28 | /** 29 | * Renders the receiver [List] to a JSON Array string 30 | * with an optional [indent] factor. 31 | * 32 | * @param indent The indent factor, default 0. 33 | * 34 | * @return The rendered JSON Array string. 35 | */ 36 | expect fun List<*>.toJsonString(indent: Int = 0): String 37 | 38 | /** 39 | * Renders the receiver [Array] to a JSON Array string 40 | * with an optional [indent] factor. 41 | * 42 | * @param indent The indent factor, default 0. 43 | * 44 | * @return The rendered JSON Array string. 45 | */ 46 | expect fun Array<*>.toJsonString(indent: Int = 0): String 47 | 48 | /** 49 | * Renders the receiver [Sequence] to a JSON Array string 50 | * with an optional [indent] factor. 51 | * 52 | * @param indent The indent factor, default 0. 53 | * 54 | * @return The rendered JSON Array string. 55 | */ 56 | expect fun Sequence<*>.toJsonString(indent: Int = 0): String -------------------------------------------------------------------------------- /common/src/test/kotlin/me/kgustave/json/test/InternalTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.internal.renderHexString 19 | import kotlin.test.Test 20 | import kotlin.test.assertEquals 21 | 22 | class InternalTests { 23 | @Test fun testRenderToHexString() { 24 | val hex = renderHexString(20) 25 | assertEquals("14", hex) 26 | } 27 | } -------------------------------------------------------------------------------- /common/src/test/kotlin/me/kgustave/json/test/ParsingTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("TestFunctionName") 17 | package me.kgustave.json.test 18 | 19 | import me.kgustave.json.JSObject 20 | import me.kgustave.json.exceptions.JSSyntaxException 21 | import me.kgustave.json.parseJsonArray 22 | import me.kgustave.json.parseJsonObject 23 | import kotlin.test.* 24 | 25 | /** 26 | * @author Kaidan Gustave 27 | */ 28 | @Ignore 29 | class ParsingTests { 30 | @Test @TestName("Parse Simple Object") fun ParseSimpleObject() { 31 | val json = parseJsonObject(""" 32 | { 33 | "foo": "bar" 34 | } 35 | """) 36 | assertTrue("foo" in json) 37 | assertEquals(json.string("foo"), "bar") 38 | } 39 | 40 | @Test @TestName("Parse Complex Object") fun ParseComplexObject() { 41 | val json = parseJsonObject(""" 42 | { 43 | "foo": null, 44 | "number": 123, 45 | "biz": { 46 | "inner": "value" 47 | } 48 | } 49 | """) 50 | 51 | assertTrue(json.isNull("foo")) 52 | assertEquals(json.int("number"), 123) 53 | assertTrue("inner" in json.obj("biz")) 54 | } 55 | 56 | @Test @TestName("Parse Object With Array") fun ParseObjectWithArray() { 57 | val json = parseJsonObject(""" 58 | { 59 | "values": [1, 5, 3], 60 | "sum": 9 61 | } 62 | """) 63 | 64 | val values = assertNotNull(json.optArray("values")) 65 | val sum = values.sumBy { assertNotNull(it as? Int) } 66 | assertEquals(sum, json.int("sum")) 67 | } 68 | 69 | @Test @TestName("Try Parse Object And Fail") fun TryParseObjectAndFail() { 70 | assertFailsWith { 71 | // call trim() to avoid intellij error from language injection 72 | parseJsonObject("""{"": }""".trim()) 73 | } 74 | } 75 | 76 | @Test @TestName("Parse Simple Array") fun ParseSimpleArray() { 77 | val json = parseJsonArray(""" 78 | [ 79 | "foo", 80 | "boo" 81 | ] 82 | """) 83 | 84 | assertEquals(json.string(0), "foo") 85 | assertEquals("foo boo", json.joinToString(separator = " ")) 86 | } 87 | 88 | @Test @TestName("Parse Complex Array") fun ParseComplexArray() { 89 | val json = parseJsonArray(""" 90 | [ 91 | [2, 5, 7], 92 | [1, 8, 4], 93 | [9, 9, 3] 94 | ] 95 | """) 96 | 97 | assertEquals(json.array(0).int(1), 5) 98 | assertEquals(json.array(2).int(0), 9) 99 | } 100 | 101 | @Test @TestName("Parse Array With Object") fun ParseArrayWithObject() { 102 | val json = parseJsonArray(""" 103 | [ 104 | { 105 | "value": "Hello", 106 | "space_after": false 107 | }, 108 | { 109 | "value": ",", 110 | "space_after": true 111 | }, 112 | { 113 | "value": "World!", 114 | "space_after": false 115 | } 116 | ] 117 | """) 118 | 119 | val result = json.joinToString(separator = "") join@ { 120 | val obj = assertNotNull(it as? JSObject) 121 | val value = obj.string("value") 122 | val spaceAfter = obj.boolean("space_after") 123 | 124 | return@join if(spaceAfter) "$value " else value 125 | } 126 | 127 | assertEquals(result, "Hello, World!") 128 | } 129 | 130 | @Test @TestName("Try Parse Array And Fail") fun TryParseArrayAndFail() { 131 | assertFailsWith { 132 | // call trim() to avoid intellij error from language injection 133 | parseJsonObject("""["incomplete]""".trim()) 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /common/src/test/kotlin/me/kgustave/json/test/TestName.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) 22 | expect annotation class TestName(val value: String) -------------------------------------------------------------------------------- /config/dokka.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | project.ext.dokkaConfig = { String... platforms = ['JVM'] -> 17 | apply plugin: 'org.jetbrains.dokka' 18 | 19 | dokka { 20 | outputFormat = 'html' 21 | outputDirectory = "$buildDir/dokka" 22 | 23 | if(!platforms.contains('JVM')) { 24 | jdkVersion = sourceCompatibility.majorVersion 25 | } 26 | impliedPlatforms = platforms 27 | 28 | noStdlibLink = false 29 | 30 | skipDeprecated = true 31 | skipEmptyPackages = true 32 | 33 | reportUndocumented = false 34 | includeNonPublic = false 35 | } 36 | 37 | task dokkaJar(type: Jar, dependsOn: dokka) { 38 | group = 'build' 39 | description = 'Generates a dokka jar' 40 | 41 | baseName = "$moduleName" 42 | version = "$version" 43 | classifier = 'javadoc' 44 | logging.level = LogLevel.QUIET 45 | from dokka.outputDirectory 46 | } 47 | 48 | build { 49 | dependsOn dokkaJar 50 | } 51 | } -------------------------------------------------------------------------------- /config/kotlin-common.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | project.ext.kotlinCommonConfig = { 17 | apply plugin: 'kotlin-platform-common' 18 | 19 | dependencies { 20 | compileOnly kotlin('stdlib-common') 21 | 22 | testCompile kotlin('test-common') 23 | testCompile kotlin('test-annotations-common') 24 | } 25 | } -------------------------------------------------------------------------------- /config/kotlin-jvm.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | project.ext.kotlinJvmConfig = { boolean common = false, int jdkVersion = 8 -> 17 | apply plugin: !common ? 'kotlin' : 'kotlin-platform-jvm' 18 | 19 | // Test with junit5 when jdk is 8+ 20 | if(jdkVersion == 8) { 21 | apply plugin: 'org.junit.platform.gradle.plugin' 22 | } 23 | 24 | sourceCompatibility = jdkVersion 25 | 26 | sourceSets { 27 | test.kotlin.srcDirs += 'src/test/kotlin' 28 | test.compileClasspath += main.compileClasspath 29 | test.runtimeClasspath += main.runtimeClasspath + main.compileClasspath 30 | } 31 | 32 | dependencies { 33 | compileOnly kotlin('stdlib') 34 | if(jdkVersion == 8) { 35 | compileOnly kotlin('stdlib-jdk8') 36 | compileOnly kotlin('stdlib-jdk7') 37 | 38 | testCompile junit('api') 39 | testCompile junit('params') 40 | testRuntime junit('engine') 41 | } else { 42 | testCompile 'junit:junit:4.12' 43 | } 44 | 45 | testCompile kotlin('test') 46 | testCompile kotlin('test-junit') 47 | } 48 | 49 | compileKotlin { 50 | kotlinOptions { 51 | jvmTarget = "1.$jdkVersion" 52 | } 53 | } 54 | 55 | compileTestKotlin { 56 | kotlinOptions { 57 | jvmTarget = "1.$jdkVersion" 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /config/kotlin2js.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | def pkgInfo = 'package-info.json' 17 | def npmBuildDir = 'build/npm' 18 | 19 | project.ext.kotlinJsConfig = { boolean common = false -> 20 | apply plugin: !common ? 'kotlin2js' : 'kotlin-platform-js' 21 | apply plugin: 'com.moowork.node' 22 | 23 | repositories { 24 | maven { url = "https://kotlin.bintray.com/kotlin-js-wrappers" } 25 | } 26 | 27 | dependencies { 28 | compileOnly kotlin('stdlib-js') 29 | testCompile kotlin('test-js') 30 | } 31 | 32 | [compileKotlin2Js, compileTestKotlin2Js]*.configure { 33 | kotlinOptions { 34 | main = 'noCall' 35 | moduleKind = 'umd' 36 | noStdlib = true 37 | metaInfo = true 38 | typedArrays = true 39 | sourceMap = true 40 | sourceMapEmbedSources = 'always' 41 | } 42 | } 43 | 44 | node { 45 | download = false 46 | npmWorkDir = file("${project.buildDir}/npm") 47 | } 48 | 49 | task installDeps(type: NpmTask) { 50 | group = 'npm' 51 | args = ['install', '--save-dev', 'mocha', 'kotlin'] 52 | } 53 | 54 | task copyReadme(type: Copy) { 55 | group = 'npm' 56 | into npmBuildDir 57 | from project.file('README.md').exists() ? project.projectDir : rootProject.projectDir 58 | include 'README.md' 59 | } 60 | 61 | task copyLicense(type: Copy) { 62 | group = 'npm' 63 | into npmBuildDir 64 | from rootProject.projectDir 65 | include 'LICENSE' 66 | } 67 | 68 | task processPkg(type: Copy) { 69 | group = 'npm' 70 | from '.' 71 | into '.' 72 | include pkgInfo 73 | 74 | def expands = [ 75 | kotlin_version: "$kotlin_version", 76 | kotlin_json_version: "$version", 77 | root_name: "${rootProject.name}", 78 | module_name: "$moduleName" 79 | ] 80 | 81 | expand(expands) 82 | rename { 83 | if(it.endsWith(pkgInfo)) { 84 | def file = file("$project.projectDir/package.json") 85 | if(!file.exists() && !file.createNewFile()) { 86 | throw new IOException("Failed to remove old package.json file!") 87 | } 88 | return 'package.json' 89 | } 90 | return it 91 | } 92 | } 93 | 94 | task processBuildPkg(type: Copy, dependsOn: [processPkg, copyReadme, copyLicense]) { 95 | group = 'npm' 96 | from processPkg.destinationDir 97 | into npmBuildDir 98 | include 'package.json' 99 | } 100 | 101 | task prepublish(type: Copy, dependsOn: [installDeps, processBuildPkg, compileKotlin2Js]) { 102 | group = 'npm' 103 | from 'build/classes/kotlin/main' 104 | into npmBuildDir 105 | } 106 | 107 | task runMocha(type: NodeTask, dependsOn: [compileTestKotlin2Js, installDeps]) { 108 | script = file('node_modules/mocha/bin/mocha') 109 | args = [compileTestKotlin2Js.outputFile] 110 | } 111 | 112 | npmInstall { 113 | dependsOn installDeps 114 | execOverrides { 115 | it.workingDir = npmBuildDir 116 | } 117 | } 118 | 119 | npm_publish { 120 | group = 'publishing' 121 | 122 | dependsOn build 123 | 124 | args = ['--access', 'public'] 125 | execOverrides { 126 | it.workingDir = npmBuildDir 127 | } 128 | } 129 | 130 | test { 131 | dependsOn runMocha 132 | } 133 | 134 | build { 135 | dependsOn processBuildPkg 136 | dependsOn prepublish 137 | } 138 | } 139 | 140 | project.ext.install = { dependency, dev = false -> 141 | //noinspection GroovyAssignabilityCheck 142 | tasks.create("npmInstall$dependency", NpmTask) { 143 | group = 'npm' 144 | description = "Installs $dependency" 145 | def a = ['install', dependency] 146 | if(dev) a.add('--save-dev') 147 | args = a 148 | } 149 | } 150 | 151 | project.ext.populateModules = { 152 | task populateNodeModules(type: Copy, dependsOn: compileKotlin2Js) { 153 | from compileKotlin2Js.destinationDir 154 | 155 | configurations.testCompile.each { 156 | from zipTree(it.absolutePath).matching { include '*.js' } 157 | } 158 | 159 | into "${buildDir}/node_modules" 160 | } 161 | 162 | runMocha { 163 | dependsOn populateNodeModules 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /core/gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Kaidan Gustave 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | deprecated=true -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/JSONDsl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * [DslMarker] for JSON closure Domain Specifying Language. 20 | * 21 | * @author Kaidan Gustave 22 | * @since 1.0 23 | */ 24 | @DslMarker 25 | annotation class JSONDsl -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/JSString.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * Interface used to stringify JSON style entities. 20 | * 21 | * The [toJsonString] provides a `indent` parameter for 22 | * pretty print JSON strings. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.0 26 | */ 27 | interface JSString { 28 | /** 29 | * Converts a value to a JSON entity as a [String] 30 | * rendered with the specified [indent] factor. 31 | */ 32 | fun toJsonString(indent: Int): String 33 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/JSWriter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * A writer for JSON entities. 20 | * 21 | * @author Kaidan Gustave 22 | * @since 1.4 23 | */ 24 | interface JSWriter: AutoCloseable { 25 | /** 26 | * The level that this writer is appending at. 27 | * 28 | * - Calls to [obj] or [array] increase this by 1. 29 | * - Calls to [endObj] or [endArray] decrease this by 1. 30 | * 31 | * When this returns 0, the writer can no longer be appended to. 32 | */ 33 | val level: Int 34 | 35 | /** 36 | * Appends the start of a JSON object. 37 | * 38 | * @return This [JSWriter]. 39 | */ 40 | fun obj(): JSWriter 41 | 42 | /** 43 | * Appends the start of a JSON array. 44 | * 45 | * @return This [JSWriter]. 46 | */ 47 | fun array(): JSWriter 48 | 49 | /** 50 | * Appends the [key] as a key for a JSON object key-value pair. 51 | * 52 | * This will require that the next method called on this [JSWriter] 53 | * is [value]. 54 | * 55 | * @param key The key to append. 56 | * 57 | * @return This [JSWriter]. 58 | */ 59 | fun key(key: String): JSWriter 60 | 61 | /** 62 | * Appends a [value] to this [JSWriter]. 63 | * 64 | * This can either be the value expected after a [key] in the context 65 | * of a JSON object, or simply another value in the context of a JSON 66 | * array. 67 | * 68 | * @param value The value to append. 69 | * 70 | * @return This [JSWriter]. 71 | */ 72 | fun value(value: Any?): JSWriter 73 | 74 | /** 75 | * Appends the end of a JSON object. 76 | * 77 | * @return This [JSWriter]. 78 | */ 79 | fun endObj(): JSWriter 80 | 81 | /** 82 | * Appends the end of a JSON array. 83 | * 84 | * @return This [JSWriter]. 85 | */ 86 | fun endArray(): JSWriter 87 | 88 | /** 89 | * Closes this writer. 90 | * 91 | * The internal implementation of this interface wraps an [Appendable], 92 | * and the implementation of this function closes that [Appendable] 93 | * granted it also implements [AutoCloseable]. 94 | * 95 | * This re-throws any exceptions that [AutoCloseable.close] might throw. 96 | */ 97 | @Throws(Throwable::class) 98 | override fun close() 99 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/exceptions/JSException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.exceptions 17 | 18 | /** 19 | * Higher level exception for all other exceptions 20 | * that come from the kotlin-json library. 21 | * 22 | * This exception has two intentions: 23 | * 24 | * 1) To serve as a superclass and a catchall for all extending exceptions. 25 | * 2) To serve as a generic exception that does not fill the criteria for throwing a subclass of it. 26 | * 27 | * Generally speaking, unless otherwise documented, this should be used whenever 28 | * dealing with catching errors from various kotlin-json resources. 29 | * 30 | * @author Kaidan Gustave 31 | * @since 1.0 32 | */ 33 | open class JSException(message: String? = null, cause: Throwable? = null): RuntimeException(message, cause) -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/exceptions/JSSyntaxException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.exceptions 17 | 18 | /** 19 | * Sub-Exception of [JSException] raised whenever a parsing JSON string 20 | * does not meet a syntax requirement and fails to be valid JSON. 21 | * 22 | * @author Kaidan Gustave 23 | * @since 1.0 24 | */ 25 | class JSSyntaxException(message: String? = null, cause: Throwable? = null): JSException(message, cause) -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/AbstractJSArray.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("MemberVisibilityCanBePrivate") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | 21 | /** 22 | * Abstract implementation of [JSArray]. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.0 26 | */ 27 | internal abstract class AbstractJSArray(protected val list: MutableList = ArrayList()): JSArray, 28 | MutableList by list { 29 | constructor(tokener: JSTokener): this() { 30 | if(tokener.nextSymbol() != '[') { 31 | tokener.syntaxError("A JSArray text must start with '['") 32 | } 33 | if(tokener.nextSymbol() != ']') { 34 | tokener.prev() 35 | while(true) { 36 | if(tokener.nextSymbol() == ',') { 37 | tokener.prev() 38 | list.add(null) 39 | } else { 40 | tokener.prev() 41 | list.add(tokener.nextValue()) 42 | } 43 | 44 | when(tokener.nextSymbol()) { 45 | ',' -> { 46 | if(tokener.nextSymbol() == ']') { 47 | return 48 | } 49 | tokener.prev() 50 | } 51 | ']' -> return 52 | else -> tokener.syntaxError("Expected a ',' or ']'") 53 | } 54 | } 55 | } 56 | } 57 | 58 | override fun add(element: Any?): Boolean = list.add(convertValue(element)) 59 | override fun add(index: Int, element: Any?) = list.add(index, convertValue(element)) 60 | override fun addAll(elements: Collection): Boolean = list.addAll(elements.map { convertValue(it) }) 61 | override fun addAll(index: Int, elements: Collection): Boolean = list.addAll(index, elements.map { convertValue(it) }) 62 | override fun set(index: Int, element: Any?): Any? { 63 | if(index == size) add(element) else return list.set(index, element) 64 | return null 65 | } 66 | 67 | override fun isNull(index: Int): Boolean { 68 | if(0 > index || index > size - 1) return true 69 | return this[index] === null 70 | } 71 | 72 | override fun toJsonString(indent: Int): String = buildJsonArrayString(indent, 0) 73 | override fun toString(): String = toJsonString(0) 74 | } 75 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/AbstractJSObject.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("MemberVisibilityCanBePrivate") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.* 20 | 21 | /** 22 | * Abstract implementation of [JSObject]. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.0 26 | */ 27 | internal abstract class AbstractJSObject( 28 | protected val map: MutableMap = HashMap() 29 | ): JSObject, MutableMap by map { 30 | constructor(tokener: JSTokener): this() { 31 | if(tokener.nextSymbol() != '{') { 32 | tokener.syntaxError("A JSObject text must begin with '{'") 33 | } 34 | 35 | var c: Char 36 | while(true) { 37 | c = tokener.nextSymbol() 38 | 39 | when(c) { 40 | JSTokener.NCHAR -> { 41 | tokener.syntaxError("A JSObject text must end with '}'") 42 | } 43 | 44 | '}' -> return 45 | } 46 | tokener.prev() 47 | val key = tokener.nextKey() 48 | map[key] = tokener.nextValue() 49 | when(tokener.nextSymbol()) { 50 | ';', ',' -> { 51 | if(tokener.nextSymbol() == '}') return 52 | tokener.prev() 53 | } 54 | 55 | '}' -> return 56 | 57 | else -> tokener.syntaxError("Expected a ',' or '}'") 58 | } 59 | } 60 | } 61 | 62 | override fun put(key: String, value: Any?): Any? = map.put(key, convertValue(value)) 63 | override fun putAll(from: Map) { 64 | from.forEach { s, u -> put(s, u) } 65 | } 66 | 67 | override fun isNull(key: String): Boolean { 68 | return map[key] === null 69 | } 70 | 71 | override fun toJsonString(indent: Int): String = buildJsonObjectString(indent) 72 | override fun toString(): String = toJsonString(0) 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/JSArrayImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * Internal implementation of [JSArray][me.kgustave.json.JSArray]. 20 | * 21 | * @author Kaidan Gustave 22 | * @version 1.0 23 | */ 24 | internal class JSArrayImpl: AbstractJSArray { 25 | constructor(array: Array<*>): super(array.mapTo(ArrayList(array.size)) { convertValue(it) }) 26 | constructor(collection: Collection<*>): super(collection.mapTo(ArrayList(collection.size)) { convertValue(it) }) 27 | constructor(tokener: JSTokener): super(tokener) 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/JSObjectImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | package me.kgustave.json.internal 18 | 19 | /** 20 | * Internal implementation of [JSObject][me.kgustave.json.JSObject]. 21 | * 22 | * @author Kaidan Gustave 23 | * @version 1.0 24 | */ 25 | internal class JSObjectImpl: AbstractJSObject { 26 | constructor(): super() 27 | constructor(map: Map): super(map.toMutableMap()) 28 | constructor(vararg pairs: Pair): super(mutableMapOf(*pairs)) 29 | constructor(tokener: JSTokener): super(tokener) 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/conversion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_ConversionKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import java.math.BigInteger 22 | import java.util.concurrent.atomic.AtomicInteger 23 | import java.util.concurrent.atomic.AtomicLong 24 | 25 | internal fun convertValue(value: Any?): Any? { 26 | return when(value) { 27 | null -> null 28 | 29 | // Place these before so that Number doesn't catch them 30 | is AtomicInteger -> value.get() 31 | is AtomicLong -> value.get() 32 | 33 | is String, is Number, is BigInteger, 34 | is Boolean, is JSObject, is JSArray -> value 35 | 36 | is Pair<*, *> -> JSObjectImpl("${value.first}" to convertValue(value.second)) 37 | is Map<*, *> -> value.entries.associateByTo(JSObjectImpl(), { 38 | it.key as? String ?: "${it.key}" 39 | }, { 40 | convertValue(it.value) 41 | }) 42 | 43 | is Collection<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 44 | is Array<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 45 | 46 | else -> throw IllegalArgumentException("${value::class} is not a valid JS type!") 47 | } 48 | } 49 | 50 | internal fun convertString(value: Any?): String? { 51 | if(value is String) return value 52 | if(value is JSObject || value is JSArray) { 53 | // shouldn't return objects or arrays as strings! 54 | return null 55 | } 56 | return value.toString() 57 | } 58 | 59 | internal fun convertBoolean(value: Any?, fromString: Boolean = false): Boolean? { 60 | if(value is Boolean) return value 61 | if(fromString && (value is String && (value == "true" || value == "false"))) { 62 | // check if it is true or false so we don't 63 | //convert a non-boolean value and return 64 | //false when it should be null 65 | return value.toBoolean() 66 | } 67 | return null 68 | } 69 | 70 | internal fun convertLong(value: Any?, fromString: Boolean = false): Long? { 71 | (value as? Long ?: (value as? Int)?.toLong())?.let { return it } 72 | if(fromString && value is String) { 73 | return value.toLongOrNull() 74 | } 75 | return null 76 | } 77 | 78 | internal fun convertInt(value: Any?, fromString: Boolean = false): Int? { 79 | (value as? Int)?.let { return it } 80 | if(fromString && value is String) { 81 | return value.toIntOrNull() 82 | } 83 | return null 84 | } 85 | 86 | internal fun convertDouble(value: Any?, fromString: Boolean = false): Double? { 87 | (value as? Double ?: (value as? Float)?.toDouble())?.let { return it } 88 | if(fromString && value is String) { 89 | return value.toDoubleOrNull() 90 | } 91 | return null 92 | } 93 | 94 | internal fun convertFloat(value: Any?, fromString: Boolean = false): Float? { 95 | (value as? Float)?.let { return it } 96 | if(fromString && value is String) { 97 | return value.toFloatOrNull() 98 | } 99 | return null 100 | } 101 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_ExceptionsKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.exceptions.JSException 20 | 21 | internal inline fun checkJson(condition: Boolean, msg: () -> String) { 22 | if(!condition) { 23 | throw JSException(msg()) 24 | } 25 | } 26 | 27 | internal inline fun checkNotNullJson(value: T?, msg: () -> String): T { 28 | if(value === null) { 29 | throw JSException(msg()) 30 | } 31 | return value 32 | } 33 | 34 | internal inline fun tryWrap(block: () -> R): R { 35 | try { 36 | return block() 37 | } catch(t: Throwable) { 38 | throw JSException(cause = t) 39 | } 40 | } 41 | 42 | internal inline fun ignored(block: () -> Unit) = try { block() } catch(ignored: Throwable) {} -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/internal/parsing.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_ParsingKt") 17 | package me.kgustave.json.internal 18 | 19 | internal fun removeComments(str: String): String { 20 | // Instead of filtering out the commented lines, we simply skip them. 21 | // This doesn't save much if any overhead but considering the efficiency 22 | // we lose from comments in general, the quickest operation should be taken. 23 | return str.split('\n').joinToString("\n") { if(it.trim().startsWith("//")) "" else it } 24 | } 25 | 26 | internal fun stringToValue(str: String): Any? { 27 | if(str.isEmpty()) { 28 | return str 29 | } 30 | 31 | when(str) { 32 | "true" -> return true 33 | "false" -> return false 34 | "null" -> return null 35 | } 36 | 37 | val initial = str[0] 38 | if(initial in '0'..'9' || initial == '-') { 39 | ignored { 40 | if(str.indexOf('.') > -1 || str.indexOf('e') > -1 || str.indexOf('E') > -1 || str == "-0") { 41 | val d = str.toDouble() 42 | if(!d.isInfinite() && !d.isNaN()) { 43 | return d 44 | } 45 | } else { 46 | val long = str.toLong() 47 | if(str == long.toString()) { 48 | return if(long == long.toInt().toLong()) long.toInt() else long 49 | } 50 | } 51 | } 52 | } 53 | return str 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/options/JSParsingOptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.options 17 | 18 | // TODO Some considerations for options: 19 | // - String2Long: Convert strings to longs automatically 20 | // - No Nulls: Whether or not nullable values are allowed when parsing 21 | // - BigInteger: Support for parsing BigIntegers 22 | // - Shorts: Support for short values 23 | 24 | /** 25 | * Options used for parsing functions. 26 | * 27 | * @author Kaidan Gustave 28 | * @since 1.2 29 | */ 30 | interface JSParsingOptions: Cloneable { 31 | /** 32 | * Whether or not parsing should support comments. 33 | * 34 | * This will filter all lines starting with `//`, 35 | * leaving only lines that don't to be parsed. 36 | * 37 | * By default this is `false`. 38 | */ 39 | val comments get() = false 40 | 41 | /** 42 | * Creates a clone of this [JSParsingOptions]. 43 | */ 44 | override fun clone(): JSParsingOptions = Copy(this) 45 | 46 | /** 47 | * Global default [JSParsingOptions]. 48 | */ 49 | companion object: JSParsingOptions { 50 | override var comments = super.comments 51 | } 52 | 53 | // Simple copy class 54 | private class Copy(base: JSParsingOptions): JSParsingOptions { 55 | override val comments = base.comments 56 | } 57 | } -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/parsing.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("JSONParsingUtil") 17 | package me.kgustave.json 18 | 19 | import me.kgustave.json.internal.* 20 | import me.kgustave.json.options.JSParsingOptions 21 | import org.intellij.lang.annotations.Language 22 | 23 | private const val JSON = "JSON" 24 | 25 | /** 26 | * Parses the provided JSON [String] into a [JSObject] using 27 | * the specified [JSParsingOptions]. 28 | * 29 | * @param options The [JSParsingOptions] to parse with. 30 | * @param json The JSON [String] to parse. 31 | * 32 | * @return A [JSObject] parsed from the JSON [String]. 33 | * 34 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 35 | */ 36 | fun parseJsonObject(options: JSParsingOptions, @Language(JSON) json: String): JSObject { 37 | val str = if(options.comments) removeComments(json) else json 38 | return JSTokener(str).use { t -> JSObjectImpl(t) } 39 | } 40 | 41 | /** 42 | * Parses the provided JSON [String] into a [JSObject]. 43 | * 44 | * @param json The JSON [String] to parse. 45 | * 46 | * @return A [JSObject] parsed from the JSON [String]. 47 | * 48 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 49 | */ 50 | fun parseJsonObject(@Language(JSON) json: String): JSObject { 51 | return parseJsonObject(JSParsingOptions, json) 52 | } 53 | 54 | /** 55 | * Parses the provided JSON [String] into a [JSArray] using 56 | * the specified [JSParsingOptions]. 57 | * 58 | * @param options The [JSParsingOptions] to parse with. 59 | * @param json The JSON [String] to parse. 60 | * 61 | * @return A [JSArray] parsed from the JSON [String]. 62 | * 63 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 64 | */ 65 | fun parseJsonArray(options: JSParsingOptions, @Language(JSON) json: String): JSArray { 66 | val str = if(options.comments) removeComments(json) else json 67 | return JSTokener(str).use { t -> JSArrayImpl(t) } 68 | } 69 | 70 | /** 71 | * Parses the provided JSON [String] into a [JSArray]. 72 | * 73 | * @param json The JSON [String] to parse. 74 | * 75 | * @return A [JSArray] parsed from the JSON [String]. 76 | * 77 | * @throws me.kgustave.json.exceptions.JSSyntaxException If the JSON [String] has invalid syntax. 78 | */ 79 | fun parseJsonArray(@Language(JSON) json: String): JSArray { 80 | return parseJsonArray(JSParsingOptions, json) 81 | } 82 | -------------------------------------------------------------------------------- /core/src/main/kotlin/me/kgustave/json/rendering.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("JSONRenderingUtil") 17 | package me.kgustave.json 18 | 19 | import me.kgustave.json.internal.buildJsonArrayString 20 | import me.kgustave.json.internal.buildJsonObjectString 21 | 22 | /** 23 | * Renders the receiver [Map] to a JSON Object string 24 | * with an optional [indent] factor. 25 | * 26 | * @param indent The indent factor, default 0. 27 | * 28 | * @return The rendered JSON Object string. 29 | */ 30 | fun Map.toJsonString(indent: Int = 0): String = buildJsonObjectString(indent) 31 | 32 | /** 33 | * Renders the receiver [List] to a JSON Array string 34 | * with an optional [indent] factor. 35 | * 36 | * @param indent The indent factor, default 0. 37 | * 38 | * @return The rendered JSON Array string. 39 | */ 40 | fun List<*>.toJsonString(indent: Int = 0): String = buildJsonArrayString(indent) 41 | 42 | /** 43 | * Renders the receiver [Array] to a JSON Array string 44 | * with an optional [indent] factor. 45 | * 46 | * @param indent The indent factor, default 0. 47 | * 48 | * @return The rendered JSON Array string. 49 | */ 50 | fun Array<*>.toJsonString(indent: Int = 0): String = buildJsonArrayString(indent) 51 | 52 | // Deprecated 53 | 54 | /** 55 | * Renders the receiver [Array] of [Pairs][Pair] to a 56 | * JSON Object string with an optional [indent] factor. 57 | * 58 | * @param indent The indent factor, default 0. 59 | * 60 | * @return The rendered JSON Object string. 61 | */ 62 | @Deprecated( 63 | message = "Uncertain conflict with star projected Array<*> overload of this extension!", 64 | replaceWith = ReplaceWith( 65 | expression = "mapOf(this).toJsonString(indent)" 66 | ), 67 | level = DeprecationLevel.WARNING 68 | ) 69 | fun Array>.toJsonString(indent: Int = 0): String = buildJsonArrayString(indent) 70 | -------------------------------------------------------------------------------- /core/src/test/kotlin/me/kgustave/json/test/constants.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import org.junit.jupiter.api.RepeatedTest 19 | 20 | internal const val TestName = RepeatedTest.DISPLAY_NAME_PLACEHOLDER 21 | internal const val TrialNumber = RepeatedTest.CURRENT_REPETITION_PLACEHOLDER 22 | internal const val TotalTrialNumber = RepeatedTest.TOTAL_REPETITIONS_PLACEHOLDER 23 | internal const val SimpleRepeatedTestName = "$TestName [$TrialNumber/$TotalTrialNumber]" -------------------------------------------------------------------------------- /core/src/test/kotlin/me/kgustave/json/test/extension/SingleInstance.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test.extension 17 | 18 | import org.junit.jupiter.api.TestInstance 19 | 20 | @Target(AnnotationTarget.CLASS) 21 | @Retention(AnnotationRetention.RUNTIME) 22 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 23 | annotation class SingleInstance -------------------------------------------------------------------------------- /core/src/test/kotlin/me/kgustave/json/test/extension/fileTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test.extension 17 | 18 | import org.junit.jupiter.api.extension.ExtensionContext 19 | import org.junit.jupiter.params.ParameterizedTest 20 | import org.junit.jupiter.params.provider.Arguments 21 | import org.junit.jupiter.params.provider.ArgumentsProvider 22 | import org.junit.jupiter.params.provider.ArgumentsSource 23 | import java.io.File 24 | import java.util.stream.Stream 25 | import kotlin.annotation.AnnotationRetention.RUNTIME 26 | import kotlin.annotation.AnnotationTarget.* 27 | 28 | @[Target(CLASS, FUNCTION, CONSTRUCTOR) Retention(RUNTIME)] 29 | @ArgumentsSource(FileProvider::class) 30 | @ParameterizedTest(name = "{arguments}") 31 | annotation class FileTest( 32 | val value: String, 33 | val createNew: Boolean = true, 34 | val deleteAfter: Boolean = true 35 | ) 36 | 37 | class FileProvider: ArgumentsProvider { 38 | private val userDir = System.getProperty("user.dir") 39 | private lateinit var file: File 40 | 41 | override fun provideArguments(context: ExtensionContext): Stream { 42 | val provideFile = checkNotNull(fileTestFromContext(context)) { "@FileTest not found!" } 43 | file = File(userDir + provideFile.value) 44 | 45 | if(provideFile.createNew) { 46 | if(file.exists()) { 47 | check(file.delete()) 48 | } 49 | check(file.createNewFile()) 50 | check(file.exists()) 51 | if(provideFile.deleteAfter) { 52 | file.deleteOnExit() 53 | } 54 | } 55 | 56 | return listOf(Arguments({ arrayOf(file) })).stream() 57 | } 58 | 59 | private fun fileTestFromContext(context: ExtensionContext): FileTest? { 60 | return context.testMethod.takeIf { it.isPresent } 61 | ?.get()?.getAnnotation(FileTest::class.java) 62 | } 63 | } -------------------------------------------------------------------------------- /core/src/test/resources/empty.txt: -------------------------------------------------------------------------------- 1 | // Empty File 2 | // This will cause TestIO#testIOError to throw an JSSyntaxException -------------------------------------------------------------------------------- /core/src/test/resources/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kaidan Gustave", 3 | "github": "TheMonitorLizard", 4 | "repositories": [ 5 | { 6 | "name": "kotlin-json", 7 | "languages": [ 8 | { 9 | "name": "Kotlin", 10 | "composition": 100.0 11 | } 12 | ], 13 | "description": "A lightweight JavaScript Object Notation (JSON) library for Kotlin JVM." 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Kaidan Gustave 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | bintrayUsername= 17 | bintrayApiKey= 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shengaero/kotlin-json/73f88bdee77dee67d7ae1a8c6181723c849327f1/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Kaidan Gustave 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | distributionBase=GRADLE_USER_HOME 17 | distributionPath=wrapper/dists 18 | zipStoreBase=GRADLE_USER_HOME 19 | zipStorePath=wrapper/dists 20 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 21 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /jdk6/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJvmConfig(true, 6) 17 | dokkaConfig('JVM') 18 | 19 | dependencies { 20 | expectedBy common() 21 | } 22 | -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/JSWriter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * A writer for JSON entities. 20 | * 21 | * @author Kaidan Gustave 22 | * @since 1.4 23 | */ 24 | interface JSWriter: AutoCloseable { 25 | /** 26 | * The level that this writer is appending at. 27 | * 28 | * - Calls to [obj] or [array] increase this by 1. 29 | * - Calls to [endObj] or [endArray] decrease this by 1. 30 | * 31 | * When this returns 0, the writer can no longer be appended to. 32 | */ 33 | val level: Int 34 | 35 | /** 36 | * Appends the start of a JSON object. 37 | * 38 | * @return This [JSWriter]. 39 | */ 40 | fun obj(): JSWriter 41 | 42 | /** 43 | * Appends the start of a JSON array. 44 | * 45 | * @return This [JSWriter]. 46 | */ 47 | fun array(): JSWriter 48 | 49 | /** 50 | * Appends the [key] as a key for a JSON object key-value pair. 51 | * 52 | * This will require that the next method called on this [JSWriter] 53 | * is [value]. 54 | * 55 | * @param key The key to append. 56 | * 57 | * @return This [JSWriter]. 58 | */ 59 | fun key(key: String): JSWriter 60 | 61 | /** 62 | * Appends a [value] to this [JSWriter]. 63 | * 64 | * This can either be the value expected after a [key] in the context 65 | * of a JSON object, or simply another value in the context of a JSON 66 | * array. 67 | * 68 | * @param value The value to append. 69 | * 70 | * @return This [JSWriter]. 71 | */ 72 | fun value(value: Any?): JSWriter 73 | 74 | /** 75 | * Appends the end of a JSON object. 76 | * 77 | * @return This [JSWriter]. 78 | */ 79 | fun endObj(): JSWriter 80 | 81 | /** 82 | * Appends the end of a JSON array. 83 | * 84 | * @return This [JSWriter]. 85 | */ 86 | fun endArray(): JSWriter 87 | 88 | @JSONDsl infix fun String.to(value: Any?) { 89 | key(this).value(value) 90 | } 91 | 92 | /** 93 | * Closes this writer. 94 | * 95 | * The internal implementation of this interface wraps an [Appendable], 96 | * and the implementation of this function closes that [Appendable] 97 | * granted it also implements [AutoCloseable]. 98 | * 99 | * This re-throws any exceptions that [AutoCloseable.close] might throw. 100 | */ 101 | @Throws(Throwable::class) 102 | override fun close() 103 | } -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/internal/JSArrayImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal actual class JSArrayImpl: AbstractJSArray { 22 | actual constructor(): super() 23 | actual constructor(array: Array<*>): this() { addAll(array) } 24 | actual constructor(collection: Collection<*>): this() { addAll(collection) } 25 | constructor(tokener: JSTokener): this() { 26 | if(tokener.nextSymbol() != '[') { 27 | tokener.syntaxError("A JSArray text must start with '['") 28 | } 29 | if(tokener.nextSymbol() != ']') { 30 | tokener.prev() 31 | while(true) { 32 | if(tokener.nextSymbol() == ',') { 33 | tokener.prev() 34 | list.add(null) 35 | } else { 36 | tokener.prev() 37 | list.add(tokener.nextValue()) 38 | } 39 | 40 | when(tokener.nextSymbol()) { 41 | ',' -> { 42 | if(tokener.nextSymbol() == ']') { 43 | return 44 | } 45 | tokener.prev() 46 | } 47 | ']' -> return 48 | else -> tokener.syntaxError("Expected a ',' or ']'") 49 | } 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/internal/JSObjectImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal actual class JSObjectImpl: AbstractJSObject { 22 | actual constructor(): super() 23 | actual constructor(map: Map): this() { putAll(map) } 24 | actual constructor(vararg pairs: Pair): this() { putAll(pairs) } 25 | constructor(tokener: JSTokener): this() { 26 | if(tokener.nextSymbol() != '{') { 27 | tokener.syntaxError("A JSObject text must begin with '{'") 28 | } 29 | 30 | var c: Char 31 | while(true) { 32 | c = tokener.nextSymbol() 33 | 34 | when(c) { 35 | JSTokener.NCHAR -> { 36 | tokener.syntaxError("A JSObject text must end with '}'") 37 | } 38 | 39 | '}' -> return 40 | } 41 | tokener.prev() 42 | val key = tokener.nextKey() 43 | map[key] = tokener.nextValue() 44 | when(tokener.nextSymbol()) { 45 | ';', ',' -> { 46 | if(tokener.nextSymbol() == '}') return 47 | tokener.prev() 48 | } 49 | 50 | '}' -> return 51 | 52 | else -> tokener.syntaxError("Expected a ',' or '}'") 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/internal/conversion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_ConversionKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import java.math.BigInteger 22 | import java.util.concurrent.atomic.AtomicInteger 23 | import java.util.concurrent.atomic.AtomicLong 24 | 25 | internal actual fun convertValue(value: Any?): Any? { 26 | return when(value) { 27 | null -> null 28 | 29 | // Place these before so that Number doesn't catch them 30 | is AtomicInteger -> value.get() 31 | is AtomicLong -> value.get() 32 | 33 | is String, is Number, is BigInteger, 34 | is Boolean, is JSObject, is JSArray -> value 35 | 36 | is Pair<*, *> -> JSObjectImpl("${value.first}" to convertValue(value.second)) 37 | is Map<*, *> -> value.entries.associateByTo(JSObjectImpl(), { 38 | it.key as? String ?: "${it.key}" 39 | }, { 40 | convertValue(it.value) 41 | }) 42 | 43 | is Collection<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 44 | is Array<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 45 | 46 | else -> throw IllegalArgumentException("${value::class} is not a valid JS type!") 47 | } 48 | } -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_ExceptionsKt") 17 | package me.kgustave.json.internal 18 | 19 | import kotlin.reflect.KClass 20 | 21 | internal actual fun isNotType(key: String, value: Any, type: KClass<*>): String { 22 | return "Value with key '$key' was ${value::class.javaPrimitiveType ?: value::class.java} " + 23 | "instead of ${type.javaPrimitiveType ?: type.java}" 24 | } 25 | 26 | internal actual fun isNotType(index: Int, value: Any, type: KClass<*>): String { 27 | return "Value with key '$index' was ${value::class.javaPrimitiveType ?: value::class.java} " + 28 | "instead of ${type.javaPrimitiveType ?: type.java}" 29 | } -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/internal/rendering.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_RenderingKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import java.lang.IllegalStateException 22 | import java.math.BigInteger 23 | import java.util.concurrent.atomic.AtomicInteger 24 | import java.util.concurrent.atomic.AtomicLong 25 | 26 | internal actual fun renderValue(value: Any?, indent: Int, level: Int, newline: Boolean): String { 27 | return when(value) { 28 | null -> value.toString() 29 | is Boolean -> value.toString() 30 | is String -> escape(value) 31 | is Int -> value.toString() 32 | is Long -> value.toString() 33 | is Float -> value.toDouble().toString() 34 | is Double -> value.toString() 35 | is BigInteger -> value.toString() 36 | is AtomicInteger -> value.get().toString() 37 | is AtomicLong -> value.get().toString() 38 | is JSObject -> value.buildJsonObjectString(indent, level + 1, newline) 39 | is JSArray -> value.buildJsonArrayString(indent, level + 1, newline) 40 | is Map<*, *> -> { 41 | @Suppress("UNCHECKED_CAST") 42 | // This might be faster to cast if the typing is correct 43 | val realMap = value as? Map ?: value.mapKeys { "${it.key}" } 44 | return realMap.buildJsonObjectString(indent, level + 1, newline) 45 | } 46 | is List<*> -> value.buildJsonArrayString(indent, level + 1, newline) 47 | is Array<*> -> value.buildJsonArrayString(indent, level + 1, newline) 48 | else -> throw IllegalStateException("Cannot convert type: ${value::class}") 49 | } 50 | } 51 | 52 | internal actual fun renderHexString(int: Int): String = Integer.toHexString(int) -------------------------------------------------------------------------------- /jdk6/src/main/kotlin/me/kgustave/json/options/JSParsingOptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.options 17 | 18 | // TODO Some considerations for options: 19 | // - String2Long: Convert strings to longs automatically 20 | // - No Nulls: Whether or not nullable values are allowed when parsing 21 | // - BigInteger: Support for parsing BigIntegers 22 | // - Shorts: Support for short values 23 | 24 | /** 25 | * Options used for parsing functions. 26 | * 27 | * @author Kaidan Gustave 28 | * @since 1.2 29 | */ 30 | interface JSParsingOptions: Cloneable { 31 | /** 32 | * Whether or not parsing should support comments. 33 | * 34 | * This will filter all lines starting with `//`, 35 | * leaving only lines that don't to be parsed. 36 | * 37 | * By default this is `false`. 38 | */ 39 | val comments get() = false 40 | 41 | /** 42 | * Creates a clone of this [JSParsingOptions]. 43 | */ 44 | override fun clone(): JSParsingOptions = Copy(this) 45 | 46 | /** 47 | * Global default [JSParsingOptions]. 48 | */ 49 | companion object: JSParsingOptions { 50 | override var comments = super.comments 51 | } 52 | 53 | // Simple copy class 54 | private class Copy(base: JSParsingOptions): JSParsingOptions { 55 | override val comments = base.comments 56 | } 57 | } -------------------------------------------------------------------------------- /jdk6/src/test/kotlin/me/kgustave/json/test/IOOperations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.JSObject 19 | import me.kgustave.json.exceptions.JSSyntaxException 20 | import me.kgustave.json.readJSObject 21 | import org.junit.Ignore 22 | import kotlin.test.Test 23 | import kotlin.test.assertEquals 24 | import kotlin.test.assertFailsWith 25 | import kotlin.test.assertNotNull 26 | 27 | @Ignore 28 | class IOOperations { 29 | @Test fun `Read JSObject From Resource`() { 30 | val url = checkNotNull(this::class.java.getResource("/test.json")) 31 | val json = url.readJSObject() 32 | 33 | assertNotNull(json["github"] as? String) 34 | val repositories = assertNotNull(json.optArray("repositories")) 35 | assertEquals(repositories.size, 1) 36 | assertNotNull(repositories[0] as? JSObject) 37 | } 38 | 39 | @Test fun `Read Failure From Invalid Syntax`() { 40 | assertFailsWith { 41 | this::class.java.getResource("/empty.txt").readJSObject() 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /jdk6/src/test/kotlin/me/kgustave/json/test/JsonTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.* 19 | import kotlin.test.Test 20 | import kotlin.test.assertEquals 21 | import kotlin.test.assertNull 22 | import kotlin.test.assertTrue 23 | 24 | /** 25 | * @author Kaidan Gustave 26 | */ 27 | class JsonTests { 28 | @Test fun `Dsl Style Creation`() { 29 | val json = JSObject { 30 | "foo" to "bar" 31 | "biz" to null 32 | "array" to JSArray(5, 10.4, "abc") 33 | "inner" to JSObject { 34 | "boo" to JSArray(4) { if(it == 1) "bar" else null } 35 | } 36 | } 37 | 38 | assertEquals(json.size, 4) 39 | assertTrue("foo" in json) 40 | assertTrue(json.isNull("biz")) 41 | assertEquals(json.array("array").size, 3) 42 | assertEquals(json.obj("inner").array("boo").count { it === null }, 3) 43 | } 44 | 45 | @Test fun `mapOf Style Creation`() { 46 | val json = jsObjectOf("foo" to "bar", "baz" to 123.4, "biz" to null) 47 | 48 | assertEquals(json.string("foo"), "bar") 49 | assertTrue("baz" in json) 50 | assertNull(json["biz"]) 51 | } 52 | 53 | @Test fun `listOf Style Creation`() { 54 | val json = jsArrayOf("abc", 123, null, "cba") 55 | 56 | assertEquals(4, json.size) 57 | for((i, v) in json.withIndex()) { 58 | when(i) { 59 | 0, 3 -> assertTrue(v is String) 60 | 1 -> assertTrue(v is Int) 61 | 2 -> assertNull(v) 62 | } 63 | } 64 | } 65 | 66 | @Test fun `Write Json`() { 67 | JSWriter().obj() 68 | .key("foo").value("bar") 69 | .endObj() 70 | } 71 | } -------------------------------------------------------------------------------- /jdk6/src/test/kotlin/me/kgustave/json/test/TestName.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | // Dummy class 19 | 20 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) 21 | actual annotation class TestName actual constructor(actual val value: String) -------------------------------------------------------------------------------- /jdk8/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJvmConfig(true) 17 | dokkaConfig('JVM') 18 | 19 | dependencies { 20 | expectedBy common() 21 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/JSWriter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json 17 | 18 | /** 19 | * A writer for JSON entities. 20 | * 21 | * @author Kaidan Gustave 22 | * @since 1.4 23 | */ 24 | interface JSWriter: AutoCloseable { 25 | /** 26 | * The level that this writer is appending at. 27 | * 28 | * - Calls to [obj] or [array] increase this by 1. 29 | * - Calls to [endObj] or [endArray] decrease this by 1. 30 | * 31 | * When this returns 0, the writer can no longer be appended to. 32 | */ 33 | val level: Int 34 | 35 | /** 36 | * Appends the start of a JSON object. 37 | * 38 | * @return This [JSWriter]. 39 | */ 40 | fun obj(): JSWriter 41 | 42 | /** 43 | * Appends the start of a JSON array. 44 | * 45 | * @return This [JSWriter]. 46 | */ 47 | fun array(): JSWriter 48 | 49 | /** 50 | * Appends the [key] as a key for a JSON object key-value pair. 51 | * 52 | * This will require that the next method called on this [JSWriter] 53 | * is [value]. 54 | * 55 | * @param key The key to append. 56 | * 57 | * @return This [JSWriter]. 58 | */ 59 | fun key(key: String): JSWriter 60 | 61 | /** 62 | * Appends a [value] to this [JSWriter]. 63 | * 64 | * This can either be the value expected after a [key] in the context 65 | * of a JSON object, or simply another value in the context of a JSON 66 | * array. 67 | * 68 | * @param value The value to append. 69 | * 70 | * @return This [JSWriter]. 71 | */ 72 | fun value(value: Any?): JSWriter 73 | 74 | /** 75 | * Appends the end of a JSON object. 76 | * 77 | * @return This [JSWriter]. 78 | */ 79 | fun endObj(): JSWriter 80 | 81 | /** 82 | * Appends the end of a JSON array. 83 | * 84 | * @return This [JSWriter]. 85 | */ 86 | fun endArray(): JSWriter 87 | 88 | @JSONDsl infix fun String.to(value: Any?) { 89 | key(this).value(value) 90 | } 91 | 92 | /** 93 | * Closes this writer. 94 | * 95 | * The internal implementation of this interface wraps an [Appendable], 96 | * and the implementation of this function closes that [Appendable] 97 | * granted it also implements [AutoCloseable]. 98 | * 99 | * This re-throws any exceptions that [AutoCloseable.close] might throw. 100 | */ 101 | @Throws(Throwable::class) 102 | override fun close() 103 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/internal/JSArrayImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal actual class JSArrayImpl: AbstractJSArray { 22 | actual constructor(): super() 23 | actual constructor(array: Array<*>): this() { addAll(array) } 24 | actual constructor(collection: Collection<*>): this() { addAll(collection) } 25 | constructor(tokener: JSTokener): this() { 26 | if(tokener.nextSymbol() != '[') { 27 | tokener.expectedArrayStart() 28 | } 29 | 30 | var c: Char 31 | while(true) { 32 | c = tokener.nextSymbol() 33 | when(c) { 34 | JSTokener.NCHAR -> tokener.expectedArrayEnd() 35 | ']' -> return 36 | } 37 | tokener.prev() 38 | list += tokener.nextValue() 39 | when(tokener.nextSymbol()) { 40 | ',' -> { 41 | if(tokener.nextSymbol() == ']') return 42 | tokener.prev() 43 | } 44 | 45 | ']' -> return 46 | 47 | else -> tokener.expectedCommaOrArrayEnd() 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/internal/JSObjectImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | internal actual class JSObjectImpl: AbstractJSObject { 19 | actual constructor(): super() 20 | actual constructor(map: Map): this() { putAll(map) } 21 | actual constructor(vararg pairs: Pair): this() { putAll(pairs) } 22 | constructor(tokener: JSTokener): this() { 23 | if(tokener.nextSymbol() != '{') { 24 | tokener.expectedObjectStart() 25 | } 26 | 27 | var c: Char 28 | while(true) { 29 | c = tokener.nextSymbol() 30 | 31 | when(c) { 32 | JSTokener.NCHAR -> { 33 | tokener.expectedObjectEnd() 34 | } 35 | 36 | '}' -> return 37 | } 38 | tokener.prev() 39 | val key = tokener.nextKey() 40 | map[key] = tokener.nextValue() 41 | when(tokener.nextSymbol()) { 42 | ',' -> { 43 | if(tokener.nextSymbol() == '}') return 44 | tokener.prev() 45 | } 46 | 47 | '}' -> return 48 | 49 | else -> tokener.expectedCommaOrObjectEnd() 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/internal/conversion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_ConversionKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import java.math.BigInteger 22 | import java.util.concurrent.atomic.AtomicInteger 23 | import java.util.concurrent.atomic.AtomicLong 24 | 25 | internal actual fun convertValue(value: Any?): Any? { 26 | return when(value) { 27 | null -> null 28 | 29 | // Place these before so that Number doesn't catch them 30 | is AtomicInteger -> value.get() 31 | is AtomicLong -> value.get() 32 | 33 | is String, is Number, is BigInteger, 34 | is Boolean, is JSObject, is JSArray -> value 35 | 36 | is Pair<*, *> -> JSObjectImpl("${value.first}" to convertValue(value.second)) 37 | is Map<*, *> -> value.entries.associateByTo(JSObjectImpl(), { 38 | it.key as? String ?: "${it.key}" 39 | }, { 40 | convertValue(it.value) 41 | }) 42 | 43 | is Collection<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 44 | is Array<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 45 | 46 | else -> throw IllegalArgumentException("${value::class} is not a valid JS type!") 47 | } 48 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_ExceptionsKt") 17 | package me.kgustave.json.internal 18 | 19 | import kotlin.reflect.KClass 20 | 21 | internal actual fun isNotType(key: String, value: Any, type: KClass<*>): String { 22 | return "Value with key '$key' was ${value::class.javaPrimitiveType ?: value::class.java} " + 23 | "instead of ${type.javaPrimitiveType ?: type.java}" 24 | } 25 | 26 | internal actual fun isNotType(index: Int, value: Any, type: KClass<*>): String { 27 | return "Value with key '$index' was ${value::class.javaPrimitiveType ?: value::class.java} " + 28 | "instead of ${type.javaPrimitiveType ?: type.java}" 29 | } -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/internal/rendering.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Actual_Jvm_RenderingKt") 17 | package me.kgustave.json.internal 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import java.lang.IllegalStateException 22 | import java.math.BigInteger 23 | import java.util.concurrent.atomic.AtomicInteger 24 | import java.util.concurrent.atomic.AtomicLong 25 | 26 | internal actual fun renderValue(value: Any?, indent: Int, level: Int, newline: Boolean): String { 27 | return when(value) { 28 | null -> value.toString() 29 | is Boolean -> value.toString() 30 | is String -> escape(value) 31 | is Int -> value.toString() 32 | is Long -> value.toString() 33 | is Float -> value.toDouble().toString() 34 | is Double -> value.toString() 35 | is BigInteger -> value.toString() 36 | is AtomicInteger -> value.get().toString() 37 | is AtomicLong -> value.get().toString() 38 | is JSObject -> value.buildJsonObjectString(indent, level + 1, newline) 39 | is JSArray -> value.buildJsonArrayString(indent, level + 1, newline) 40 | is Map<*, *> -> { 41 | @Suppress("UNCHECKED_CAST") 42 | // This might be faster to cast if the typing is correct 43 | val realMap = value as? Map ?: value.mapKeys { "${it.key}" } 44 | return realMap.buildJsonObjectString(indent, level + 1, newline) 45 | } 46 | is List<*> -> value.buildJsonArrayString(indent, level + 1, newline) 47 | is Array<*> -> value.buildJsonArrayString(indent, level + 1, newline) 48 | else -> throw IllegalStateException("Cannot convert type: ${value::class}") 49 | } 50 | } 51 | 52 | internal actual fun renderHexString(int: Int): String = Integer.toHexString(int) -------------------------------------------------------------------------------- /jdk8/src/main/kotlin/me/kgustave/json/options/JSParsingOptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.options 17 | 18 | // TODO Some considerations for options: 19 | // - String2Long: Convert strings to longs automatically 20 | // - No Nulls: Whether or not nullable values are allowed when parsing 21 | // - BigInteger: Support for parsing BigIntegers 22 | // - Shorts: Support for short values 23 | 24 | /** 25 | * Options used for parsing functions. 26 | * 27 | * @author Kaidan Gustave 28 | * @since 1.2 29 | */ 30 | interface JSParsingOptions: Cloneable { 31 | /** 32 | * Whether or not parsing should support comments. 33 | * 34 | * This will filter all lines starting with `//`, 35 | * leaving only lines that don't to be parsed. 36 | * 37 | * By default this is `false`. 38 | */ 39 | val comments get() = false 40 | 41 | /** 42 | * Creates a clone of this [JSParsingOptions]. 43 | */ 44 | override fun clone(): JSParsingOptions = Copy(this) 45 | 46 | /** 47 | * Global default [JSParsingOptions]. 48 | */ 49 | companion object: JSParsingOptions { 50 | override var comments = super.comments 51 | } 52 | 53 | // Simple copy class 54 | private class Copy(base: JSParsingOptions): JSParsingOptions { 55 | override val comments = base.comments 56 | } 57 | } -------------------------------------------------------------------------------- /jdk8/src/test/kotlin/me/kgustave/json/test/IOOperations.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.JSObject 19 | import me.kgustave.json.exceptions.JSSyntaxException 20 | import me.kgustave.json.readJSObject 21 | import org.junit.jupiter.api.Test 22 | import kotlin.test.assertEquals 23 | import kotlin.test.assertFailsWith 24 | import kotlin.test.assertNotNull 25 | 26 | class IOOperations { 27 | @Test fun `Read JSObject From Resource`() { 28 | val url = checkNotNull(this::class.java.getResource("/test.json")) 29 | val json = url.readJSObject() 30 | 31 | assertNotNull(json["github"] as? String) 32 | val repositories = assertNotNull(json.optArray("repositories")) 33 | assertEquals(repositories.size, 1) 34 | assertNotNull(repositories[0] as? JSObject) 35 | } 36 | 37 | @Test fun `Read Failure From Invalid Syntax`() { 38 | assertFailsWith { 39 | this::class.java.getResource("/empty.txt").readJSObject() 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /jdk8/src/test/kotlin/me/kgustave/json/test/TestName.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import org.junit.jupiter.api.DisplayName 19 | 20 | actual typealias TestName = DisplayName -------------------------------------------------------------------------------- /jdk8/src/test/kotlin/me/kgustave/json/test/constants.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import org.junit.jupiter.api.RepeatedTest 19 | 20 | internal const val TrialNumber = RepeatedTest.CURRENT_REPETITION_PLACEHOLDER 21 | internal const val TotalTrialNumber = RepeatedTest.TOTAL_REPETITIONS_PLACEHOLDER 22 | internal const val SimpleRepeatedTestName = 23 | "${RepeatedTest.DISPLAY_NAME_PLACEHOLDER} [$TrialNumber/$TotalTrialNumber]" -------------------------------------------------------------------------------- /jdk8/src/test/kotlin/me/kgustave/json/test/extension/SingleInstance.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test.extension 17 | 18 | import org.junit.jupiter.api.TestInstance 19 | 20 | @Target(AnnotationTarget.CLASS) 21 | @Retention(AnnotationRetention.RUNTIME) 22 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 23 | annotation class SingleInstance -------------------------------------------------------------------------------- /jdk8/src/test/kotlin/me/kgustave/json/test/extension/fileTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test.extension 17 | 18 | import org.junit.jupiter.api.extension.ExtensionContext 19 | import org.junit.jupiter.params.ParameterizedTest 20 | import org.junit.jupiter.params.provider.Arguments 21 | import org.junit.jupiter.params.provider.ArgumentsProvider 22 | import org.junit.jupiter.params.provider.ArgumentsSource 23 | import java.io.File 24 | import java.util.stream.Stream 25 | import kotlin.annotation.AnnotationRetention.RUNTIME 26 | import kotlin.annotation.AnnotationTarget.* 27 | 28 | @[Target(CLASS, FUNCTION, CONSTRUCTOR) Retention(RUNTIME)] 29 | @ArgumentsSource(FileProvider::class) 30 | @ParameterizedTest(name = "{arguments}") 31 | annotation class FileTest( 32 | val value: String, 33 | val createNew: Boolean = true, 34 | val deleteAfter: Boolean = true 35 | ) 36 | 37 | class FileProvider: ArgumentsProvider { 38 | private val userDir = System.getProperty("user.dir") 39 | private lateinit var file: File 40 | 41 | override fun provideArguments(context: ExtensionContext): Stream { 42 | val provideFile = checkNotNull(fileTestFromContext(context)) { "@FileTest not found!" } 43 | file = File(userDir + provideFile.value) 44 | 45 | if(provideFile.createNew) { 46 | if(file.exists()) { 47 | check(file.delete()) 48 | } 49 | check(file.createNewFile()) 50 | check(file.exists()) 51 | if(provideFile.deleteAfter) { 52 | file.deleteOnExit() 53 | } 54 | } 55 | 56 | return listOf(Arguments { arrayOf(file) }).stream() 57 | } 58 | 59 | private fun fileTestFromContext(context: ExtensionContext): FileTest? { 60 | return context.testMethod.takeIf { it.isPresent } 61 | ?.get()?.getAnnotation(FileTest::class.java) 62 | } 63 | } -------------------------------------------------------------------------------- /jdk8/src/test/resources/empty.txt: -------------------------------------------------------------------------------- 1 | // Empty File 2 | // This will cause TestIO#testIOError to throw an JSSyntaxException -------------------------------------------------------------------------------- /jdk8/src/test/resources/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Kaidan Gustave", 3 | "github": "TheMonitorLizard", 4 | "repositories": [ 5 | { 6 | "name": "kotlin-json", 7 | "languages": [ 8 | { 9 | "name": "Kotlin", 10 | "composition": 100.0 11 | } 12 | ], 13 | "description": "A lightweight JavaScript Object Notation (JSON) library for Kotlin JVM." 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /js/README.md: -------------------------------------------------------------------------------- 1 | [npm]: https://img.shields.io/npm/v/npm.svg 2 | [license]: https://img.shields.io/badge/License-Apache%202.0-lightgrey.svg 3 | [discord]: https://discord.gg/XCmwxy8 4 | [discord-widget]: https://discordapp.com/api/guilds/301012120613552138/widget.png 5 | 6 | [ ![npm][] ](https://www.npmjs.com/package/kotlin-json) 7 | [ ![license][] ](https://github.com/Shengaero/kotlin-json/tree/master/LICENSE) 8 | [ ![Awesome Kotlin](https://kotlin.link/awesome-kotlin.svg) ](https://github.com/KotlinBy/awesome-kotlin) 9 | 10 | [ ![Discord][discord-widget] ][discord] 11 | 12 | # kotlin-json-js 13 | 14 | [Kotlin JSON](https://github.com/Shengaero/kotlin-json) for JavaScript target. 15 | 16 | ## Installation 17 | 18 | `npm i kotlin-json` 19 | 20 | If you are using Intellij IDEA and [`create-react-kotlin-app`](https://github.com/JetBrains/create-react-kotlin-app), 21 | you can run the following to have your IDE resolve the library: 22 | 23 | `npm run gen-idea-libs` 24 | 25 | ## License 26 | 27 | kotlin-json is licensed under the Apache 2.0 License 28 | 29 | ``` 30 | Copyright 2018 Kaidan Gustave 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); 33 | you may not use this file except in compliance with the License. 34 | You may obtain a copy of the License at 35 | 36 | http://www.apache.org/licenses/LICENSE-2.0 37 | 38 | Unless required by applicable law or agreed to in writing, software 39 | distributed under the License is distributed on an "AS IS" BASIS, 40 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 41 | See the License for the specific language governing permissions and 42 | limitations under the License. 43 | ``` 44 | -------------------------------------------------------------------------------- /js/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJsConfig(true) 17 | 18 | dependencies { 19 | expectedBy common() 20 | } 21 | 22 | prepublish { 23 | rename { 24 | def dotIndex = it.indexOf('.') 25 | if(dotIndex == -1) { 26 | return it 27 | } 28 | def fileExt = it.substring(dotIndex) 29 | if(it.startsWith('kotlin-json-')) { 30 | return 'kotlin-json' + fileExt 31 | } 32 | return it 33 | } 34 | 35 | doLast { 36 | def kjsmDir = file("$project.buildDir/npm/$moduleName") 37 | copy { 38 | def kjsmTree = fileTree(kjsmDir) 39 | kjsmTree.include '**/*.kjsm' 40 | from kjsmTree 41 | into file("$project.buildDir/npm/$rootProject.name") 42 | } 43 | 44 | delete(kjsmDir) 45 | } 46 | } 47 | 48 | populateModules() -------------------------------------------------------------------------------- /js/package-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$root_name", 3 | "version": "$kotlin_json_version", 4 | "description": "A lightweight, highly stylized JavaScript Object Notation (JSON) for The Kotlin Programming Language.", 5 | "author": "Kaidan Gustave", 6 | "license": "Apache-2.0", 7 | "keywords": [ 8 | "kotlin", "kotlin2js", "json", "kotlin-json", 9 | "dsl", "shengaero", "kgustave" 10 | ], 11 | "homepage": "https://github.com/Shengaero/kotlin-json#readme", 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/Shengaero/kotlin-json" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/Shengaero/kotlin-json/issues" 18 | }, 19 | "main": "${root_name}.js", 20 | "files": [ 21 | "**/**/*.kjsm", 22 | "*.js", 23 | "*.js.map", 24 | "*.meta.js", 25 | "LICENSE", 26 | "package.json", 27 | "README.md" 28 | ], 29 | "peerDependencies": { 30 | "kotlin": "^$kotlin_version" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/JSArrayImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | internal actual class JSArrayImpl: AbstractJSArray { 22 | actual constructor(): super() 23 | actual constructor(array: Array): this() { addAll(array) } 24 | actual constructor(collection: Collection): this() { addAll(collection) } 25 | } -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/JSObjectImpl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import kotlin.js.Json 19 | 20 | /** 21 | * @author Kaidan Gustave 22 | */ 23 | internal actual class JSObjectImpl: AbstractJSObject { 24 | actual constructor(): super() 25 | actual constructor(map: Map): this() { putAll(map) } 26 | actual constructor(vararg pairs: Pair): this() { putAll(pairs) } 27 | constructor(json: Json): this() { 28 | for(key in js("Object.getOwnPropertyNames(json)")) { 29 | val k = key.toString() 30 | this[k] = json[k] 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/conversion.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import me.kgustave.json.JSArray 19 | import me.kgustave.json.JSObject 20 | import me.kgustave.json.jsonObject 21 | 22 | internal actual fun convertValue(value: Any?): Any? { 23 | return when(value) { 24 | null -> null 25 | 26 | is String, is Number, is Boolean, is JSObject, is JSArray -> value 27 | 28 | is Pair<*, *> -> JSObjectImpl("${value.first}" to convertValue(value.second)) 29 | is Map<*, *> -> value.entries.associateByTo(JSObjectImpl(), 30 | { it.key as? String ?: "${it.key}" }, { convertValue(it.value) }) 31 | 32 | is Collection<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 33 | is Array<*> -> JSArrayImpl(value.mapTo(ArrayList(value.size)) { convertValue(it) }) 34 | 35 | else -> convertValue(jsonObject(value)) 36 | } 37 | } -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import kotlin.reflect.KClass 19 | 20 | internal actual fun isNotType(key: String, value: Any, type: KClass<*>): String { 21 | return "Value with key '$key' was ${value::class.js.name} instead of ${type.js.name}" 22 | } 23 | 24 | internal actual fun isNotType(index: Int, value: Any, type: KClass<*>): String { 25 | return "Value with key '$index' was ${value::class.js.name} instead of ${type.js.name}" 26 | } -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/hacks.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | private var checked = false 19 | private var node = false 20 | 21 | // true if we are using node.js 22 | internal fun isNodeEcosystem(): Boolean { 23 | if(!checked) { 24 | @Suppress("CanBeVal") 25 | var isNode = false 26 | js("""if(typeof module !== 'undefined' && module.exports) { isNode = true }""") 27 | node = isNode 28 | checked = true 29 | } 30 | return node 31 | } -------------------------------------------------------------------------------- /js/src/main/kotlin/me/kgustave/json/internal/rendering.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.internal 17 | 18 | import me.kgustave.json.JSArray 19 | import me.kgustave.json.JSObject 20 | 21 | internal actual fun renderValue(value: Any?, indent: Int, level: Int, newline: Boolean): String { 22 | return when(value) { 23 | null -> value.toString() 24 | is Boolean -> value.toString() 25 | is String -> escape(value) 26 | is Int -> value.toString() 27 | is Long -> value.toString() 28 | is Float -> value.toDouble().toString() 29 | is Double -> value.toString() 30 | is JSObject -> value.buildJsonObjectString(indent, level + 1, newline) 31 | is JSArray -> value.buildJsonArrayString(indent, level + 1, newline) 32 | is Map<*, *> -> { 33 | @Suppress("UNCHECKED_CAST") 34 | // This might be faster to cast if the typing is correct 35 | val realMap = value as? Map ?: value.mapKeys { "${it.key}" } 36 | return realMap.buildJsonObjectString(indent, level + 1, newline) 37 | } 38 | is List<*> -> value.buildJsonArrayString(indent, level + 1, newline) 39 | is Array<*> -> value.buildJsonArrayString(indent, level + 1, newline) 40 | else -> throw IllegalStateException("Cannot convert type: ${value::class}") 41 | } 42 | } 43 | 44 | internal actual fun renderHexString(int: Int) = js("int.toString(16)") as String -------------------------------------------------------------------------------- /js/src/test/kotlin/me/kgustave/json/test/IOTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | package me.kgustave.json.test 18 | 19 | import me.kgustave.json.readJSArray 20 | import me.kgustave.json.readJSObject 21 | import me.kgustave.json.test.mocha.describe 22 | import kotlin.test.Test 23 | import kotlin.test.assertEquals 24 | import kotlin.test.assertTrue 25 | 26 | /** 27 | * @author Kaidan Gustave 28 | */ 29 | class IOTests { 30 | private val testResources = "src/test/resources" 31 | 32 | @Test fun readJSObjectFromFile() = describe("Read an object from a json file") { 33 | val json = readJSObject("$testResources/object.json") 34 | assertTrue("foo" in json) 35 | assertEquals("bar", json.optString("foo")) 36 | } 37 | 38 | @Test fun readJSArrayFromFile() = describe("Read an array from a json file") { 39 | val json = readJSArray("$testResources/array.json") 40 | assertTrue(json.isNotEmpty()) 41 | assertTrue(json.isNull(1)) 42 | assertEquals("baz", json[2]) 43 | } 44 | } -------------------------------------------------------------------------------- /js/src/test/kotlin/me/kgustave/json/test/TestName.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | /** 19 | * @author Kaidan Gustave 20 | */ 21 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) 22 | actual annotation class TestName actual constructor(actual val value: String) -------------------------------------------------------------------------------- /js/src/test/kotlin/me/kgustave/json/test/mocha/api.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JsModule("mocha") 17 | @file:JsNonModule 18 | package me.kgustave.json.test.mocha 19 | 20 | external val describe: ContextDefinition 21 | external val it: TestDefinition 22 | 23 | external interface MochaDone 24 | external interface SuiteBuilder 25 | external interface TestBuilder: SuiteBuilder 26 | 27 | external interface ContextDefinition: SuiteBuilder { 28 | val only: SuiteBuilder 29 | val skip: SuiteBuilder 30 | fun timeout(ms: Number) 31 | } 32 | 33 | external interface Runnable { 34 | var title: String 35 | var fn: Function<*> 36 | var async: Boolean 37 | var sync: Boolean 38 | var timedOut: Boolean 39 | } 40 | 41 | external interface Suite { 42 | var parent: Suite 43 | var title: String 44 | fun fullTitle(): String 45 | } 46 | 47 | external interface Test: Runnable { 48 | var parent: Suite 49 | var pending: Boolean 50 | var state: String? /* "failed" | "passed" | undefined */ 51 | fun fullTitle(): String 52 | } 53 | 54 | external interface TestDefinition: TestBuilder { 55 | val only: TestBuilder 56 | val skip: TestBuilder 57 | var state: String /* "failed" | "passed" */ 58 | fun timeout(ms: Number) 59 | } -------------------------------------------------------------------------------- /js/src/test/kotlin/me/kgustave/json/test/mocha/dsl.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test.mocha 17 | 18 | @DslMarker 19 | internal annotation class MochaDsl 20 | 21 | @MochaDsl 22 | fun TestBuilder.async(description: String, callback: (MochaDone) -> Unit): R { 23 | return asDynamic()(description, callback).unsafeCast() 24 | } 25 | 26 | @MochaDsl 27 | operator fun SuiteBuilder.invoke(description: String, callback: () -> Unit): R { 28 | return asDynamic()(description, callback).unsafeCast() 29 | } 30 | 31 | @MochaDsl 32 | operator fun MochaDone.invoke(error: Throwable): dynamic = asDynamic()(error) 33 | 34 | @MochaDsl 35 | operator fun MochaDone.invoke(): dynamic = asDynamic()() 36 | 37 | @MochaDsl 38 | fun it(description: String, skip: Boolean = false, callback: () -> Unit) { 39 | if(skip) { 40 | return it.skip(description, callback) 41 | } 42 | it.only(description, callback) 43 | } 44 | 45 | @MochaDsl 46 | fun describe(description: String, callback: () -> Unit): Suite = describe.only(description, callback) 47 | 48 | @MochaDsl 49 | fun async(description: String, skip: Boolean = false, callback: (MochaDone) -> Unit) { 50 | if(skip) { 51 | return it.skip(description) { callback(EmptyDone) } 52 | } 53 | it.async(description, callback) 54 | } 55 | 56 | object EmptyDone: MochaDone -------------------------------------------------------------------------------- /js/src/test/resources/array.json: -------------------------------------------------------------------------------- 1 | [ 2 | 123, 3 | null, 4 | "baz" 5 | ] -------------------------------------------------------------------------------- /js/src/test/resources/object.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "bar" 3 | } -------------------------------------------------------------------------------- /libraries/ktor/client/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJvmConfig() 17 | dokkaConfig() 18 | 19 | repositories { 20 | ktorBintray() 21 | } 22 | 23 | dependencies { 24 | compileOnly ktor('client-core') 25 | compileOnly ktor('client-websocket') 26 | compileOnly ktor('client-json') 27 | compile jdk8() 28 | compile reflect() 29 | compile ktorCommon() 30 | 31 | testCompile ktor('server-core') 32 | testCompile ktor('server-netty') 33 | testCompile ktor('client-jetty') 34 | testCompile ktor('client-apache') 35 | testCompile ktor('client-cio') 36 | testCompile ktor('client-tests') 37 | testCompile ktorServer() 38 | } 39 | 40 | kotlin { 41 | experimental { 42 | coroutines = 'ENABLE' 43 | } 44 | } 45 | 46 | compileKotlin { 47 | kotlinOptions { 48 | freeCompilerArgs = [ 49 | useExperimental('kotlin.Experimental'), 50 | useExperimental('me.kgustave.json.reflect.JSSerialization'), 51 | useExperimental('me.kgustave.json.reflect.JSDeserialization') 52 | ] 53 | } 54 | } 55 | 56 | compileTestKotlin { 57 | kotlinOptions { 58 | freeCompilerArgs = [ 59 | useExperimental('kotlin.Experimental'), 60 | useExperimental('me.kgustave.json.reflect.JSSerialization'), 61 | useExperimental('me.kgustave.json.reflect.JSDeserialization') 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /libraries/ktor/client/src/main/kotlin/me/kgustave/json/ktor/client/JSKtorSerializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.ktor.client 17 | 18 | import io.ktor.client.call.TypeInfo 19 | import io.ktor.client.features.json.JsonSerializer 20 | import io.ktor.client.response.HttpResponse 21 | import io.ktor.client.response.readText 22 | import io.ktor.content.OutgoingContent 23 | import io.ktor.http.charset 24 | import me.kgustave.json.JSArray 25 | import me.kgustave.json.JSObject 26 | import me.kgustave.json.ktor.JSOutgoingContent 27 | import me.kgustave.json.parseJsonArray 28 | import me.kgustave.json.parseJsonObject 29 | import me.kgustave.json.reflect.JSDeserializer 30 | import me.kgustave.json.reflect.JSSerializer 31 | import java.nio.charset.Charset 32 | import kotlin.reflect.full.isSubclassOf 33 | 34 | class JSKtorSerializer( 35 | private val serializer: JSSerializer = JSSerializer(), 36 | private val deserializer: JSDeserializer = JSDeserializer(), 37 | private val charset: Charset = Charsets.UTF_8 38 | ): JsonSerializer { 39 | override suspend fun read(type: TypeInfo, response: HttpResponse): Any { 40 | val klass = type.type 41 | val charset = response.charset() ?: this.charset 42 | val text = response.readText(charset) 43 | if(klass.isSubclassOf(JSArray::class)) return parseJsonArray(text) 44 | val json = parseJsonObject(text) 45 | if(klass.isSubclassOf(JSObject::class)) return json 46 | return deserializer.deserialize(json, klass) 47 | } 48 | 49 | override fun write(data: Any): OutgoingContent { 50 | val json = ((data as? JSObject ?: data as? JSArray) ?: serializer.serialize(data)) 51 | return JSOutgoingContent(json, charset) 52 | } 53 | } -------------------------------------------------------------------------------- /libraries/ktor/client/src/main/kotlin/me/kgustave/json/ktor/client/extension.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.ktor.client 17 | 18 | import io.ktor.client.request.HttpRequestBuilder 19 | import me.kgustave.json.JSONDsl 20 | import me.kgustave.json.JSObject 21 | 22 | @JSONDsl inline fun HttpRequestBuilder.json(block: JSObject.() -> Unit) { 23 | this.body = JSObject(block) 24 | } -------------------------------------------------------------------------------- /libraries/ktor/client/src/test/kotlin/me/kgustave/json/ktor/client/test/extension/engineProvider.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.ktor.client.test.extension 17 | 18 | import io.ktor.client.engine.HttpClientEngineFactory 19 | import org.junit.jupiter.api.extension.ExtensionContext 20 | import org.junit.jupiter.params.ParameterizedTest 21 | import org.junit.jupiter.params.provider.Arguments 22 | import org.junit.jupiter.params.provider.ArgumentsProvider 23 | import org.junit.jupiter.params.provider.ArgumentsSource 24 | import java.lang.annotation.Inherited 25 | import java.util.stream.Stream 26 | import kotlin.annotation.AnnotationRetention.RUNTIME 27 | import kotlin.annotation.AnnotationTarget.FUNCTION 28 | import kotlin.reflect.KTypeProjection 29 | import kotlin.reflect.full.createType 30 | import kotlin.reflect.full.findAnnotation 31 | import kotlin.reflect.full.valueParameters 32 | import kotlin.reflect.jvm.kotlinFunction 33 | import io.ktor.client.engine.apache.Apache as ApacheFactory 34 | import io.ktor.client.engine.cio.CIO as CIOFactory 35 | import io.ktor.client.engine.jetty.Jetty as JettyFactory 36 | import org.junit.platform.commons.util.PreconditionViolationException as Violation 37 | 38 | @Inherited 39 | @Target(FUNCTION) 40 | @Retention(RUNTIME) 41 | @ArgumentsSource(EngineProvider::class) 42 | @ParameterizedTest(name = "[{index}] - {0}") 43 | annotation class EngineTest(val values: Array = [ 44 | EngineType.APACHE, 45 | EngineType.CIO, 46 | EngineType.JETTY 47 | ]) 48 | 49 | enum class EngineType(val factory: HttpClientEngineFactory<*>) { 50 | APACHE(ApacheFactory), 51 | CIO(CIOFactory), 52 | JETTY(JettyFactory) 53 | } 54 | 55 | internal class EngineProvider: ArgumentsProvider { 56 | private companion object { 57 | private val targetType = HttpClientEngineFactory::class.createType(listOf(KTypeProjection.STAR)) 58 | } 59 | override fun provideArguments(context: ExtensionContext): Stream { 60 | val function = context.requiredTestMethod.kotlinFunction ?: 61 | throw Violation("${context.displayName} is not a kotlin function!") 62 | val parameter = function.valueParameters.takeIf { it.size == 1 }?.get(0) ?: 63 | throw Violation("${context.displayName} must have a single parameter with type $targetType!") 64 | if(parameter.type != targetType) { 65 | throw Violation("${context.displayName} must have a single parameter with type $targetType!") 66 | } 67 | val engineTest = function.findAnnotation() ?: 68 | throw Violation("${context.displayName} does not have an @EngineTest annotation!") 69 | return engineTest.values.toList().stream().map { Arguments { arrayOf(it.factory) } } 70 | } 71 | } -------------------------------------------------------------------------------- /libraries/ktor/common/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright 2018 Kaidan Gustave 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | kotlinJvmConfig() 18 | dokkaConfig() 19 | 20 | repositories { 21 | ktorBintray() 22 | } 23 | 24 | dependencies { 25 | compileOnly ktor('http') 26 | compileOnly ktor('http-cio') 27 | compile jdk8() 28 | compile reflect() 29 | } 30 | 31 | kotlin { 32 | experimental { 33 | coroutines = 'ENABLE' 34 | } 35 | } 36 | 37 | compileKotlin { 38 | kotlinOptions { 39 | freeCompilerArgs = [ 40 | useExperimental('kotlin.Experimental'), 41 | useExperimental('me.kgustave.json.reflect.JSSerialization'), 42 | useExperimental('me.kgustave.json.reflect.JSDeserialization') 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /libraries/ktor/common/src/main/kotlin/me/kgustave/json/ktor/JSOutgoingContent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.ktor 17 | 18 | import io.ktor.cio.write 19 | import io.ktor.content.OutgoingContent 20 | import io.ktor.http.ContentType 21 | import io.ktor.http.charset 22 | import io.ktor.http.withCharset 23 | import kotlinx.coroutines.experimental.io.ByteWriteChannel 24 | import me.kgustave.json.JSString 25 | import java.nio.charset.Charset 26 | 27 | /** 28 | * Represents outgoing JSON content. 29 | * 30 | * @since 1.6 31 | * @author Kaidan Gustave 32 | */ 33 | class JSOutgoingContent(json: JSString, charset: Charset): OutgoingContent.WriteChannelContent() { 34 | private val jsonText by lazy { json.toJsonString(0) } 35 | override val contentType = ContentType.Application.Json.withCharset(charset) 36 | override val contentLength = jsonText.length.toLong() 37 | override suspend fun writeTo(channel: ByteWriteChannel) = 38 | channel.write(jsonText, contentType.charset()!!) 39 | } -------------------------------------------------------------------------------- /libraries/ktor/common/src/main/kotlin/me/kgustave/json/ktor/websocket/frames.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("FramesUtil") 17 | package me.kgustave.json.ktor.websocket 18 | 19 | import io.ktor.http.cio.websocket.Frame 20 | import kotlinx.io.core.BytePacketBuilder 21 | import me.kgustave.json.* 22 | import java.nio.charset.Charset 23 | 24 | fun JSObject.asTextFrame(fin: Boolean = false): Frame.Text { 25 | val packet = BytePacketBuilder() 26 | JSWriter(packet).obj { 27 | for((key, value) in this@asTextFrame) { 28 | key(key).value(value) 29 | } 30 | } 31 | return Frame.Text(fin, packet.build()) 32 | } 33 | 34 | fun JSObject.asBinaryFrame(fin: Boolean = false): Frame.Binary { 35 | val packet = BytePacketBuilder() 36 | JSWriter(packet).obj { 37 | for((key, value) in this@asBinaryFrame) { 38 | key(key).value(value) 39 | } 40 | } 41 | return Frame.Binary(fin, packet.build()) 42 | } 43 | 44 | fun JSArray.asTextFrame(fin: Boolean = false): Frame.Text { 45 | val packet = BytePacketBuilder() 46 | JSWriter(packet).array { 47 | for(value in this@asTextFrame) { 48 | value(value) 49 | } 50 | } 51 | return Frame.Text(fin, packet.build()) 52 | } 53 | 54 | fun JSArray.asBinaryFrame(fin: Boolean = false): Frame.Binary { 55 | val packet = BytePacketBuilder() 56 | JSWriter(packet).array { 57 | for(value in this@asBinaryFrame) { 58 | value(value) 59 | } 60 | } 61 | return Frame.Binary(fin, packet.build()) 62 | } 63 | 64 | fun Frame.Text.readJSObject(): JSObject = this.buffer.readJSObject(Charsets.UTF_8) 65 | fun Frame.Text.readJSArray(): JSArray = this.buffer.readJSArray(Charsets.UTF_8) 66 | fun Frame.Binary.readJSObject(charset: Charset = Charsets.UTF_8): JSObject = this.buffer.readJSObject(charset) 67 | fun Frame.Binary.readJSArray(charset: Charset = Charsets.UTF_8): JSArray = this.buffer.readJSArray(charset) -------------------------------------------------------------------------------- /libraries/ktor/common/src/main/kotlin/me/kgustave/json/ktor/websocket/sessions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("SessionsUtil") 17 | package me.kgustave.json.ktor.websocket 18 | 19 | import io.ktor.http.cio.websocket.WebSocketSession 20 | import me.kgustave.json.JSArray 21 | import me.kgustave.json.JSONDsl 22 | import me.kgustave.json.JSObject 23 | 24 | @JSONDsl suspend inline fun WebSocketSession.sendJSObject(block: JSObject.() -> Unit) = 25 | sendJSObject(false, block) 26 | 27 | @JSONDsl suspend inline fun WebSocketSession.sendJSArray(vararg values: Any?) = 28 | sendJSArray(false, *values) 29 | 30 | @JSONDsl suspend inline fun WebSocketSession.sendJSArray(size: Int, block: (Int) -> Any?) = 31 | sendJSArray(false, size, block) 32 | 33 | @JSONDsl suspend inline fun WebSocketSession.sendJSObject(fin: Boolean, block: JSObject.() -> Unit) = 34 | send(JSObject(block).asTextFrame(fin)) 35 | 36 | @JSONDsl suspend inline fun WebSocketSession.sendJSArray(fin: Boolean, vararg values: Any?) = 37 | send(JSArray(*values).asTextFrame(fin)) 38 | 39 | @JSONDsl suspend inline fun WebSocketSession.sendJSArray(fin: Boolean, size: Int, block: (Int) -> Any?) = 40 | send(JSArray(size, block).asTextFrame(fin)) -------------------------------------------------------------------------------- /libraries/ktor/server/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJvmConfig() 17 | dokkaConfig() 18 | 19 | repositories { 20 | ktorBintray() 21 | } 22 | 23 | dependencies { 24 | compileOnly ktor('server-core') 25 | compileOnly ktor('http-cio') 26 | compile jdk8() 27 | compile reflect() 28 | compile ktorCommon() 29 | 30 | testCompile ktor('server-test-host') 31 | } 32 | 33 | kotlin { 34 | experimental { 35 | coroutines = 'ENABLE' 36 | } 37 | } 38 | 39 | compileKotlin { 40 | kotlinOptions { 41 | freeCompilerArgs = [ 42 | useExperimental('kotlin.Experimental'), 43 | useExperimental('me.kgustave.json.reflect.JSSerialization'), 44 | useExperimental('me.kgustave.json.reflect.JSDeserialization') 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /libraries/ktor/server/src/main/kotlin/me/kgustave/json/ktor/server/JSContentConverter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("RedundantCompanionReference") 17 | package me.kgustave.json.ktor.server 18 | 19 | import io.ktor.application.ApplicationCall 20 | import io.ktor.application.call 21 | import io.ktor.features.ContentConverter 22 | import io.ktor.features.suitableCharset 23 | import io.ktor.http.ContentType 24 | import io.ktor.pipeline.PipelineContext 25 | import io.ktor.request.ApplicationReceiveRequest 26 | import io.ktor.request.ContentTransformationException 27 | import io.ktor.request.contentCharset 28 | import kotlinx.coroutines.experimental.io.ByteReadChannel 29 | import kotlinx.coroutines.experimental.io.readRemaining 30 | import kotlinx.io.core.readText 31 | import me.kgustave.json.* 32 | import me.kgustave.json.exceptions.JSSyntaxException 33 | import me.kgustave.json.ktor.JSOutgoingContent 34 | import me.kgustave.json.reflect.JSDeserializer 35 | import me.kgustave.json.reflect.JSSerializer 36 | import java.nio.charset.Charset 37 | import kotlin.reflect.KClass 38 | import kotlin.reflect.full.isSubclassOf 39 | 40 | /** 41 | * Converts JSON request bodies into [JSObjects][JSObject], 42 | * as well as JSObjects into text content for responses. 43 | * 44 | * Additionally, [serialization][JSSerializer] and 45 | * [deserialization][JSDeserializer] is available. 46 | * 47 | * @author Kaidan Gustave 48 | */ 49 | class JSContentConverter( 50 | private val deserializer: JSDeserializer = JSDeserializer(), 51 | private val serializer: JSSerializer = JSSerializer(), 52 | private var charset: Charset = Charsets.UTF_8 53 | ): ContentConverter { 54 | fun registerDeserializable(klass: KClass<*>) { 55 | deserializer.register(klass) 56 | } 57 | 58 | fun registerSerializable(klass: KClass<*>) { 59 | serializer.register(klass) 60 | } 61 | 62 | fun charset(charset: Charset) { 63 | this.charset = charset 64 | } 65 | 66 | override suspend fun convertForReceive(context: PipelineContext): Any? { 67 | val subject = context.subject 68 | val type = subject.type 69 | val channel = subject.value as? ByteReadChannel ?: return null 70 | val charset = context.call.request.contentCharset() ?: charset 71 | val text = channel.readRemaining().readText(charset.newDecoder()).takeIf(String::isNotBlank) ?: return null 72 | try { 73 | if(type.isSubclassOf(JSArray::class)) return parseJsonArray(text) 74 | val json = parseJsonObject(text) 75 | if(type.isSubclassOf(JSObject::class)) return json 76 | return deserializer.deserialize(json, type) 77 | } catch(e: JSSyntaxException) { 78 | throw ContentTransformationException("Could not parse JSON!") 79 | } 80 | } 81 | 82 | override suspend fun convertForSend(context: PipelineContext, 83 | contentType: ContentType, value: Any): Any? { 84 | if(!ContentType.Application.Json.match(contentType.withoutParameters())) return null 85 | val charset = context.context.suitableCharset(charset) 86 | val json = value as? JSObject ?: value as? JSArray ?: serializer.serialize(value) 87 | return JSOutgoingContent(json, charset) 88 | } 89 | } -------------------------------------------------------------------------------- /libraries/ktor/server/src/main/kotlin/me/kgustave/json/ktor/server/extension.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | 18 | package me.kgustave.json.ktor.server 19 | 20 | import io.ktor.application.ApplicationCall 21 | import io.ktor.http.HttpStatusCode 22 | import io.ktor.response.respond 23 | import me.kgustave.json.JSArray 24 | import me.kgustave.json.JSONDsl 25 | import me.kgustave.json.JSObject 26 | 27 | @JSONDsl suspend inline fun ApplicationCall.respondJSObject(status: HttpStatusCode, block: JSObject.() -> Unit) = 28 | respond(status, JSObject(block)) 29 | 30 | @JSONDsl suspend inline fun ApplicationCall.respondJSObject(block: JSObject.() -> Unit) = 31 | respond(JSObject(block)) 32 | 33 | @JSONDsl suspend fun ApplicationCall.respondJSArray(status: HttpStatusCode, vararg values: Any?) = 34 | respond(status, JSArray(values)) 35 | 36 | @JSONDsl suspend fun ApplicationCall.respondJSArray(vararg values: Any?) = 37 | respond(JSArray(values)) 38 | 39 | @JSONDsl suspend inline fun ApplicationCall.respondJSArray(status: HttpStatusCode, size: Int, block: (Int) -> Any?) = 40 | respond(status, JSArray(size, block)) 41 | 42 | @JSONDsl suspend inline fun ApplicationCall.respondJSArray(size: Int, block: (Int) -> Any?) = 43 | respond(JSArray(size, block)) -------------------------------------------------------------------------------- /libraries/ktor/server/src/test/kotlin/me/kgustave/json/ktor/server/test/serverTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.ktor.server.test 17 | 18 | import io.ktor.application.Application 19 | import io.ktor.application.call 20 | import io.ktor.application.install 21 | import io.ktor.config.MapApplicationConfig 22 | import io.ktor.features.ContentNegotiation 23 | import io.ktor.http.ContentType 24 | import io.ktor.http.HttpMethod 25 | import io.ktor.http.HttpStatusCode 26 | import io.ktor.http.charset 27 | import io.ktor.request.receive 28 | import io.ktor.response.respond 29 | import io.ktor.response.respondBytes 30 | import io.ktor.routing.get 31 | import io.ktor.routing.post 32 | import io.ktor.routing.route 33 | import io.ktor.routing.routing 34 | import io.ktor.server.testing.contentType 35 | import io.ktor.server.testing.handleRequest 36 | import io.ktor.server.testing.setBody 37 | import io.ktor.server.testing.withTestApplication 38 | import me.kgustave.json.JSWriter 39 | import me.kgustave.json.ktor.server.JSContentConverter 40 | import me.kgustave.json.obj 41 | import me.kgustave.json.readJSObject 42 | import org.junit.jupiter.api.Disabled 43 | import org.junit.jupiter.api.DisplayName 44 | import org.junit.jupiter.api.TestInstance 45 | import org.junit.jupiter.params.ParameterizedTest 46 | import org.junit.jupiter.params.provider.ValueSource 47 | import kotlin.test.assertEquals 48 | import kotlin.test.assertNotNull 49 | 50 | fun Application.testModule() { 51 | (environment.config as? MapApplicationConfig)?.apply { 52 | put("ktor.deployment.port", "9090") 53 | put("ktor.deployment.host", "0.0.0.0") 54 | 55 | put("ktor.application.id", KotlinJsonKtorServerTests::class.qualifiedName!!) 56 | 57 | put("ktor.deployment.shutdown.url", "/shutdown") 58 | } 59 | 60 | install(ContentNegotiation) { 61 | register(ContentType.Application.Json, JSContentConverter()) { 62 | charset(Charsets.UTF_8) 63 | registerDeserializable(Account::class) 64 | registerSerializable(Account::class) 65 | registerDeserializable(User::class) 66 | registerSerializable(User::class) 67 | } 68 | } 69 | } 70 | 71 | data class Account(val id: Long, val user: User) 72 | 73 | data class User(val name: String, val age: Int) 74 | 75 | @Disabled 76 | @DisplayName("Kotlin Json Ktor Tests") 77 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 78 | class KotlinJsonKtorServerTests { 79 | private val accounts = mutableMapOf() 80 | 81 | @ValueSource(longs = [4141245532L, 512423534L, 2412412400L]) 82 | @ParameterizedTest fun `Normal Content Conversion`(testId: Long): Unit = withTestApplication(Application::testModule) { 83 | application.routing { 84 | route("/accounts/{id}") { 85 | get { 86 | val id = requireNotNull(call.parameters["id"]?.toLongOrNull()) { "Invalid account ID" } 87 | val account = requireNotNull(accounts[id]) { "Account not found" } 88 | call.respond(status = HttpStatusCode.OK, message = account) 89 | } 90 | 91 | post { 92 | val id = requireNotNull(call.parameters["id"]?.toLongOrNull()) { "Invalid account ID" } 93 | require(id !in accounts) { "Account with ID already present" } 94 | val user = call.receive() 95 | accounts[id] = Account(id, user) 96 | call.respondBytes(bytes = ByteArray(0), status = HttpStatusCode.Created) 97 | } 98 | } 99 | } 100 | 101 | handleRequest(method = HttpMethod.Post, uri = "/accounts/$testId") { 102 | val json = JSWriter().obj({ 103 | key("name").value("Kaidan") 104 | key("age").value(19) 105 | }).toString() 106 | this.setBody(json.toByteArray(Charsets.UTF_8)) 107 | } 108 | 109 | val call = handleRequest(method = HttpMethod.Get, uri = "/accounts/$testId") 110 | val byteBody = assertNotNull(call.response.byteContent) 111 | val json = byteBody.readJSObject(call.response.contentType().charset() ?: Charsets.UTF_8) 112 | val account = assertNotNull(accounts[json.long("id")]) 113 | assertEquals(account.user.name, json.obj("user").string("name")) 114 | assertEquals(account.user.age, json.obj("user").int("age")) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /reflect/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | kotlinJvmConfig() 17 | dokkaConfig() 18 | 19 | dependencies { 20 | compileOnly kotlin('reflect') 21 | compile jdk8() 22 | } 23 | 24 | def compilerArgs = [ 25 | useExperimental('kotlin.Experimental'), 26 | useExperimental('me.kgustave.json.reflect.JSSerialization'), 27 | useExperimental('me.kgustave.json.reflect.JSDeserialization') 28 | ] 29 | 30 | compileKotlin { 31 | kotlinOptions { 32 | freeCompilerArgs = compilerArgs 33 | } 34 | } 35 | 36 | compileTestKotlin { 37 | kotlinOptions { 38 | freeCompilerArgs = compilerArgs 39 | } 40 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSConstructor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import kotlin.annotation.AnnotationRetention.* 19 | import kotlin.annotation.AnnotationTarget.* 20 | 21 | /** 22 | * Specifies which constructor should be used during deserialization 23 | * of a [JSObject][me.kgustave.json.JSObject] into an instance of the 24 | * class. 25 | * 26 | * Without this annotation, any available constructor may be used, notably the 27 | * first one returned by [KClass.constructors][kotlin.reflect.KClass.constructors]. 28 | * 29 | * Additionally, this annotation may be applied to the actual class to signify 30 | * the primary constructor should be used, and this annotation does not work 31 | * on [data classes](https://kotlinlang.org/docs/reference/data-classes.html). 32 | * 33 | * @author Kaidan Gustave 34 | * @since 1.6 35 | */ 36 | @JSDeserialization 37 | @MustBeDocumented 38 | @Retention(RUNTIME) 39 | @Target(CLASS, CONSTRUCTOR) 40 | annotation class JSConstructor -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSDeserializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | package me.kgustave.json.reflect 18 | 19 | import me.kgustave.json.JSObject 20 | import me.kgustave.json.reflect.internal.checkIfReflectionIsInClasspath 21 | import me.kgustave.json.reflect.internal.deserialization.DeserializationCache 22 | import kotlin.reflect.KClass 23 | import kotlin.reflect.full.cast 24 | 25 | /** 26 | * Deserializer for [JSObjects][JSObject]. 27 | * 28 | * @author Kaidan Gustave 29 | * @since 1.5 30 | */ 31 | @JSDeserialization 32 | class JSDeserializer { 33 | // Remain internal for testing. 34 | internal val cache = DeserializationCache(this) 35 | 36 | @JSDeserialization 37 | fun deserialize(json: JSObject, klass: KClass): T { 38 | checkIfReflectionIsInClasspath() 39 | return klass.cast(cache.construct(klass, json)) 40 | } 41 | 42 | @JSDeserialization 43 | fun register(klass: KClass) { 44 | checkIfReflectionIsInClasspath() 45 | cache.register(klass) 46 | } 47 | 48 | @JSDeserialization 49 | fun isRegistered(klass: KClass<*>) = cache.isRegistered(klass) 50 | } 51 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSName.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import kotlin.annotation.AnnotationRetention.* 19 | import kotlin.annotation.AnnotationTarget.* 20 | 21 | /** 22 | * Used to key for a JSON object value explicitly. 23 | * 24 | * `@JSName` specifies the key used with a [JSObject][me.kgustave.json.JSObject] provided when 25 | * serializing/deserializing it into/from a class instance. 26 | * 27 | * This is mostly useful for values who's names may not represent a Kotlin/Java based naming 28 | * scheme for things like web-APIs. 29 | * 30 | * Take for example the following class definition: 31 | * 32 | * ```kotlin 33 | * data class Order(val id: Long, @JSName("customer_name") val customer: String) 34 | * ``` 35 | * 36 | * ## Deserialization 37 | * 38 | * Normally, constructor parameter values are resolved during deserialization based on their 39 | * [parameter name][kotlin.reflect.KParameter.name] as the key retrieved from a `JSObject`. 40 | * 41 | * Because of this, there must exist a means of specifying the key to use. 42 | * 43 | * Take for example the following JSON object representing a customer's order on a website: 44 | * 45 | * ```json 46 | * { 47 | * "id": 2252912595123, 48 | * "customer_name": "Mark Lee" 49 | * } 50 | * ``` 51 | * 52 | * Notice the name of the `customer_name` value. Normally for this to be mapped to a parameter, 53 | * the parameter would need to be `customer_name: String`. 54 | * 55 | * For this to be properly deserialized without a change in the parameter name, you must 56 | * apply the `@JSName` with the corresponding value key to use as shown above. 57 | * 58 | * ## Serialization 59 | * 60 | * Much like deserialization, `@JSName` can be used to specify the output key name 61 | * for a property value to serialize into a `JSObject` output from the serialization 62 | * process. 63 | * 64 | * Using the same example shown above in: 65 | * 66 | * ```kotlin 67 | * fun main(args: Array) { 68 | * val serializer = JSSerializer() 69 | * val order: Order = findAnyOrder() 70 | * val json = serializer.serialize(order) 71 | * println("customer" in json) 72 | * println("customer_name" in json) 73 | * } 74 | * 75 | * // false 76 | * // true 77 | * ``` 78 | * 79 | * @author Kaidan Gustave 80 | * @since 1.5 81 | */ 82 | @JSSerialization 83 | @JSDeserialization 84 | @MustBeDocumented 85 | @Retention(RUNTIME) 86 | @Target(PROPERTY, VALUE_PARAMETER) 87 | annotation class JSName(val value: String) -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSOptional.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import kotlin.annotation.AnnotationRetention.* 19 | import kotlin.annotation.AnnotationTarget.* 20 | 21 | /** 22 | * Marks a property that is to be considered optional 23 | * when returning `null` during serialization. 24 | * 25 | * By default the [serializer][me.kgustave.json.reflect.JSSerializer] considers 26 | * all properties available to be required for serialization. 27 | * 28 | * The `@JSOptional` annotation is used to dictate that a property 29 | * returning `null` should be excluded from the serialization. 30 | * 31 | * ```kotlin 32 | * data class User( 33 | * val id: Long, 34 | * @JSName("account_name") val accountName: String, 35 | * @JSName("display_name") @JSOptional val displayName: String? = null 36 | * ) 37 | * 38 | * fun main(args: Array) { 39 | * val user1 = User(123, "user1", "userOne") 40 | * val user2 = User(321, "user2") 41 | * 42 | * val serializer = JSSerializer() 43 | * 44 | * println("User 1:\n${serializer.serialize(user1).toJsonString(2)}") 45 | * println() 46 | * println("User 2:\n${serializer.serialize(user2).toJsonString(2)}") 47 | * } 48 | * 49 | * // User 1: 50 | * // { 51 | * // "id": 123, 52 | * // "account_name": "user1", 53 | * // "display_name": "userOne" 54 | * // } 55 | * // 56 | * // User 2: 57 | * // { 58 | * // "id": 321, 59 | * // "account_name": "user2" 60 | * // } 61 | * ``` 62 | * 63 | * @author Kaidan Gustave 64 | * @since 1.5 65 | */ 66 | @JSSerialization 67 | @MustBeDocumented 68 | @Retention(RUNTIME) 69 | @Target(PROPERTY) 70 | annotation class JSOptional -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSSerializable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import kotlin.annotation.AnnotationRetention.* 19 | import kotlin.annotation.AnnotationTarget.* 20 | 21 | /** 22 | * @author Kaidan Gustave 23 | * @since 1.6 24 | */ 25 | @JSSerialization 26 | @MustBeDocumented 27 | @Target(CLASS) 28 | @Retention(RUNTIME) 29 | annotation class JSSerializable -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSSerializer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | package me.kgustave.json.reflect 18 | 19 | import me.kgustave.json.JSObject 20 | import me.kgustave.json.reflect.internal.checkIfReflectionIsInClasspath 21 | import me.kgustave.json.reflect.internal.reflectionError 22 | import me.kgustave.json.reflect.internal.serialization.SerializationCache 23 | import kotlin.reflect.KClass 24 | 25 | /** 26 | * Serializer for [JSObjects][JSObject]. 27 | * 28 | * @author Kaidan Gustave 29 | * @since 1.5 30 | */ 31 | @JSSerialization 32 | class JSSerializer @JvmOverloads constructor(configure: JSSerializer.Config.() -> Unit = {}) { 33 | // Remain internal for testing. 34 | internal val cache = SerializationCache(this) 35 | 36 | @JSSerialization 37 | val isSafe: Boolean 38 | 39 | init { 40 | val config = Config().apply(configure) 41 | this.isSafe = config.isSafe 42 | } 43 | 44 | @JSSerialization 45 | fun serialize(any: Any): JSObject { 46 | checkIfReflectionIsInClasspath() 47 | when(any) { 48 | is String, is Boolean, is Number -> reflectionError { 49 | "Cannot convert simplistic/primitive type: ${any::class}!" 50 | } 51 | } 52 | return cache.destruct(any) 53 | } 54 | 55 | @JSSerialization 56 | fun register(klass: KClass<*>) { 57 | checkIfReflectionIsInClasspath() 58 | cache.register(klass) 59 | } 60 | 61 | @JSSerialization 62 | fun isRegistered(klass: KClass<*>) = cache.isRegistered(klass) 63 | 64 | /** 65 | * Configurations for [JSSerializer]. 66 | * 67 | * @author Kaidan Gustave 68 | * @since 1.6 69 | */ 70 | @JSSerialization 71 | class Config internal constructor() { 72 | /** 73 | * Makes the configured JSSerializer safe from types 74 | * not marked with [@JSSerializable][JSSerializable]. 75 | */ 76 | @JSSerialization 77 | var isSafe = false 78 | } 79 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/JSValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import kotlin.annotation.AnnotationRetention.* 19 | import kotlin.annotation.AnnotationTarget.* 20 | 21 | /** 22 | * Explicitly specifies that a property is a JSValue. 23 | * 24 | * Normally a value 25 | * 26 | * @author Kaidan Gustave 27 | * @since 1.5 28 | */ 29 | @JSSerialization 30 | @MustBeDocumented 31 | @Retention(RUNTIME) 32 | @Target(PROPERTY) 33 | annotation class JSValue -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/exceptions/JSReflectionException.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.exceptions 17 | 18 | import me.kgustave.json.exceptions.JSException 19 | 20 | /** 21 | * Indicates an error related to reflective operations has occurred. 22 | * 23 | * @author Kaidan Gustave 24 | * @since 1.5 25 | */ 26 | class JSReflectionException(message: String? = null, cause: Throwable? = null): JSException(message, cause) -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/experimental.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmMultifileClass() 17 | package me.kgustave.json.reflect 18 | 19 | import kotlin.annotation.AnnotationRetention.* 20 | import kotlin.annotation.AnnotationTarget.* 21 | 22 | /** 23 | * Marks an annotation that is used in 24 | * class-to-JSON serialization processes. 25 | * 26 | * @author Kaidan Gustave 27 | * @since 1.5 28 | */ 29 | @MustBeDocumented 30 | @Retention(SOURCE) 31 | @Target(ANNOTATION_CLASS, CLASS, FUNCTION, PROPERTY) 32 | @Experimental(Experimental.Level.WARNING) 33 | annotation class JSSerialization 34 | 35 | /** 36 | * Marks an annotation that is used in 37 | * JSON-to-class deserialization processes. 38 | * 39 | * @author Kaidan Gustave 40 | * @since 1.5 41 | */ 42 | @MustBeDocumented 43 | @Retention(SOURCE) 44 | @Target(ANNOTATION_CLASS, CLASS, FUNCTION, PROPERTY) 45 | @Experimental(Experimental.Level.WARNING) 46 | annotation class JSDeserialization 47 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/extension.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect 17 | 18 | import me.kgustave.json.JSObject 19 | 20 | // JSDeserializer 21 | 22 | @JSDeserialization 23 | inline fun JSDeserializer.deserialize(json: JSObject): T = deserialize(json, T::class) 24 | @JSDeserialization 25 | inline fun JSDeserializer.register() = register(T::class) 26 | 27 | // JSSerializer 28 | 29 | @JSSerialization 30 | inline fun JSSerializer.register() = register(T::class) 31 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/InstanceParameterMap.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal 17 | 18 | import kotlin.reflect.KParameter 19 | 20 | /** 21 | * Stupidly simple one-to-one map for instance parameter 22 | * used to call a getter or parameter-less function. 23 | * 24 | * @author Kaidan Gustave 25 | * @since 1.5 26 | */ 27 | internal class InstanceParameterMap(parameter: KParameter, instance: Any): Map { 28 | private val entry: Map.Entry = object: Map.Entry { 29 | override val key get() = parameter 30 | override val value get() = instance 31 | override fun toString() = "$key=$value" 32 | } 33 | override val size get() = 1 34 | override val values get() = listOf(entry.value) 35 | override val keys get() = setOf(entry.key) 36 | override val entries get() = setOf(entry) 37 | override fun isEmpty(): Boolean = false 38 | override fun containsKey(key: KParameter): Boolean = key == entry.key 39 | override fun containsValue(value: Any): Boolean = value == entry.value 40 | override fun get(key: KParameter): Any? = entry.value.takeIf { key in this } 41 | 42 | override fun toString(): String = "($entry)" 43 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/NotAValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal 17 | 18 | /** 19 | * Object instance used to indicate that the returns 20 | * should not be included a default value (or no value) 21 | * should be used instead. 22 | * 23 | * @author Kaidan Gustave 24 | * @since 1.5 25 | */ 26 | internal object NotAValue { 27 | override fun toString(): String = "me.kgustave.json.reflect.internal.NotAValue" 28 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/deserialization/DeserializationType.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal.deserialization 17 | 18 | import me.kgustave.json.JSObject 19 | 20 | internal enum class DeserializationType(val call: (JSObject, String) -> Any?) { 21 | STRING({ json, key -> json.optString(key) }), 22 | INT({ json, key -> json.optInt(key) }), 23 | LONG({ json, key -> json.optLong(key) }), 24 | FLOAT({ json, key -> json.optFloat(key) }), 25 | DOUBLE({ json, key -> json.optDouble(key) }), 26 | OBJECT({ json, key -> json.optObj(key) }), 27 | ARRAY({ json, key -> json.optArray(key) }), 28 | OTHER({ _, _ -> throw IllegalStateException("This should not be called!") }); 29 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/deserialization/ParameterResolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal.deserialization 17 | 18 | import me.kgustave.json.JSObject 19 | import me.kgustave.json.reflect.internal.NotAValue 20 | import me.kgustave.json.reflect.internal.reflectionError 21 | import kotlin.reflect.KFunction 22 | import kotlin.reflect.KParameter 23 | 24 | internal class ParameterResolver( 25 | internal val key: String, 26 | internal val parameter: KParameter, 27 | private val optional: Boolean, 28 | private val nullable: Boolean, 29 | private val call: (JSObject, String) -> Any? 30 | ) { 31 | internal fun resolve(json: JSObject): Any? { 32 | if(optional && key !in json) return NotAValue 33 | val value = call(json, key) 34 | if(value === null) { 35 | if(nullable && key in json) return null 36 | reflectionError { "Could not resolve '$key' value for $parameter!" } 37 | } 38 | return value 39 | } 40 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/exceptions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_ExceptionsKt") 17 | package me.kgustave.json.reflect.internal 18 | 19 | import me.kgustave.json.reflect.exceptions.JSReflectionException 20 | 21 | inline fun reflectionError(message: () -> String): Nothing { 22 | throw JSReflectionException(message()) 23 | } 24 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/hacks.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_HacksKt") 17 | package me.kgustave.json.reflect.internal 18 | 19 | // LOCATING KOTLIN-REFLECT IN CLASSPATH 20 | 21 | // Have we found it yet? 22 | private var foundKotlinReflectInClasspath = false 23 | 24 | // Checks if kotlin-reflect is in the classpath. 25 | //If it's not, we throw a KotlinReflectionNotSupportedError 26 | internal fun checkIfReflectionIsInClasspath() { 27 | if(foundKotlinReflectInClasspath) return 28 | try { Class.forName("kotlin.reflect.jvm.internal.KClassImpl") } catch(e: ClassNotFoundException) { 29 | // Error message is generated by kotlin-reflect 30 | //as part of the no-args constructor. 31 | // In the future we might alter this to be more 32 | //informative towards our functions with the 33 | //library. 34 | throw KotlinReflectionNotSupportedError() 35 | } 36 | foundKotlinReflectInClasspath = true 37 | } 38 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/serialization/PropertyHandler.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal.serialization 17 | 18 | import me.kgustave.json.reflect.JSSerializer 19 | import me.kgustave.json.reflect.internal.InstanceParameterMap 20 | import me.kgustave.json.reflect.internal.NotAValue 21 | import kotlin.reflect.KProperty1 22 | import kotlin.reflect.full.instanceParameter 23 | 24 | internal class PropertyHandler( 25 | private val serializer: JSSerializer, 26 | private val property: KProperty1, 27 | private val optional: Boolean 28 | ) { 29 | internal fun handle(instance: Any): Any? { 30 | val parameters = InstanceParameterMap(property.instanceParameter!!, instance) 31 | val returns = property.callBy(parameters) 32 | if(returns === null) return if(optional) NotAValue else null 33 | return serializer.convertValue(returns) 34 | } 35 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/serialization/SerializationCache.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal.serialization 17 | 18 | import me.kgustave.json.JSObject 19 | import me.kgustave.json.reflect.* 20 | import me.kgustave.json.reflect.internal.reflectionError 21 | import java.lang.reflect.Modifier 22 | import kotlin.reflect.KClass 23 | import kotlin.reflect.KVisibility.INTERNAL 24 | import kotlin.reflect.KVisibility.PUBLIC 25 | import kotlin.reflect.full.findAnnotation 26 | import kotlin.reflect.full.memberProperties 27 | 28 | internal class SerializationCache(private val serializer: JSSerializer) { 29 | private val strategies = mutableMapOf, SerializationStrategy>() 30 | 31 | internal fun register(klass: KClass<*>) { 32 | if(klass !in strategies) { 33 | registerInternally(klass) 34 | } 35 | } 36 | 37 | internal fun destruct(any: Any): JSObject { 38 | val klass = any::class 39 | checkClassIsValidToDestruct(klass) 40 | return (strategies[klass] ?: registerInternally(klass)).destruct(any) 41 | } 42 | 43 | internal fun isRegistered(klass: KClass<*>): Boolean = klass in strategies 44 | 45 | private fun registerInternally(klass: KClass<*>): SerializationStrategy { 46 | check(!serializer.isSafe || klass.annotations.any { it is JSSerializable }) { 47 | "$klass is not marked with @JSSerializable!" 48 | } 49 | 50 | var valid = klass.memberProperties.filter { 51 | it.visibility == PUBLIC || it.visibility == INTERNAL 52 | } 53 | 54 | valid.filter { it.findAnnotation() !== null }.takeUnless { it.isEmpty() }?.let { valid = it } 55 | 56 | val properties = valid.associateBy({ it.findAnnotation()?.value ?: it.name }) p@ { 57 | return@p PropertyHandler(serializer, it, it.findAnnotation() !== null) 58 | } 59 | val strategy = SerializationStrategy(serializer = serializer, properties = properties) 60 | check(klass !in strategies) { "Already registered type: $klass" } 61 | strategies[klass] = strategy 62 | return strategy 63 | } 64 | 65 | private companion object { 66 | @JvmStatic private fun checkClassIsValidToDestruct(klass: KClass<*>) { 67 | // class checks 68 | when { 69 | klass.visibility != PUBLIC && klass.visibility != INTERNAL -> { 70 | val debugVisibility = klass.visibility?.toString()?.toLowerCase() ?: when { 71 | Modifier.isProtected(klass.java.modifiers) -> "protected" 72 | else -> "package-private" 73 | } 74 | reflectionError { "Unable to create $debugVisibility class instances: $klass!" } 75 | } 76 | klass.isSealed -> reflectionError { "Unable to create sealed class instances: $klass!" } 77 | klass.isAbstract -> reflectionError { "Unable to create abstract class instances: $klass!" } 78 | } 79 | } 80 | } 81 | 82 | // Internal for testing 83 | internal fun clearStrategies() { 84 | strategies.clear() 85 | } 86 | } -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/serialization/SerializationStrategy.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.reflect.internal.serialization 17 | 18 | import me.kgustave.json.JSObject 19 | import me.kgustave.json.jsObjectOf 20 | import me.kgustave.json.reflect.JSSerializer 21 | import me.kgustave.json.reflect.internal.NotAValue 22 | 23 | /** 24 | * @author Kaidan Gustave 25 | * @since 1.5 26 | */ 27 | internal class SerializationStrategy( 28 | private val serializer: JSSerializer, 29 | private val properties: Map 30 | ) { 31 | internal fun destruct(instance: Any): JSObject { 32 | val json = jsObjectOf() 33 | for((key, handler) in properties) { 34 | val value = handler.handle(instance) 35 | if(value === NotAValue) continue 36 | 37 | json[key] = serializer.convertValue(value) 38 | } 39 | return json 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /reflect/src/main/kotlin/me/kgustave/json/reflect/internal/serialization/convertValue.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:JvmName("Internal_ConvertValueKt") 17 | package me.kgustave.json.reflect.internal.serialization 18 | 19 | import me.kgustave.json.JSArray 20 | import me.kgustave.json.JSObject 21 | import me.kgustave.json.jsArrayOf 22 | import me.kgustave.json.jsObjectOf 23 | import me.kgustave.json.reflect.JSSerializer 24 | import java.math.BigInteger 25 | import java.util.concurrent.atomic.AtomicInteger 26 | import java.util.concurrent.atomic.AtomicLong 27 | 28 | internal fun JSSerializer.convertValue(value: Any?): Any? { 29 | when(value) { 30 | null -> return null 31 | 32 | is AtomicInteger -> return value.get() 33 | is AtomicLong -> return value.get() 34 | 35 | is String, is Number, 36 | is Boolean, is BigInteger, 37 | is JSObject, is JSArray -> return value 38 | 39 | is Map<*, *> -> { 40 | val json = jsObjectOf() 41 | for((k, v) in value) { 42 | json[k as? String ?: k.toString()] = convertValue(v) 43 | } 44 | return json 45 | } 46 | is Pair<*, *> -> return jsObjectOf( 47 | (value.first as? String ?: value.first.toString()) to convertValue(value.second) 48 | ) 49 | 50 | is Collection<*> -> return value.mapTo(jsArrayOf()) { convertValue(it) } 51 | is Array<*> -> return value.mapTo(jsArrayOf()) { convertValue(it) } 52 | 53 | else -> return serialize(value) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /reflect/src/test/kotlin/me/kgustave/json/test/Book.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("unused") 17 | package me.kgustave.json.test 18 | 19 | import me.kgustave.json.reflect.JSConstructor 20 | 21 | @JSConstructor 22 | class Book(val name: String, val pages: Int, val author: String?) { 23 | constructor(name: String, pages: Int): this(name, pages, null) 24 | } -------------------------------------------------------------------------------- /reflect/src/test/kotlin/me/kgustave/json/test/House.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.reflect.JSSerializable 19 | 20 | @JSSerializable 21 | data class House( 22 | val address: String, 23 | val tenants: List 24 | ) -------------------------------------------------------------------------------- /reflect/src/test/kotlin/me/kgustave/json/test/Person.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package me.kgustave.json.test 17 | 18 | import me.kgustave.json.reflect.JSOptional 19 | import me.kgustave.json.reflect.JSSerializable 20 | 21 | @JSSerializable 22 | data class Person( 23 | val name: String, 24 | val age: Int, 25 | @JSOptional val mother: Person? = null, 26 | @JSOptional val father: Person? = null 27 | ) -------------------------------------------------------------------------------- /reflect/src/test/kotlin/me/kgustave/json/test/ReflectionTests.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | @file:Suppress("MayBeConstant", "unused") 17 | 18 | package me.kgustave.json.test 19 | 20 | import me.kgustave.json.JSArray 21 | import me.kgustave.json.JSObject 22 | import me.kgustave.json.reflect.JSDeserializer 23 | import me.kgustave.json.reflect.JSSerializer 24 | import me.kgustave.json.reflect.deserialize 25 | import me.kgustave.json.reflect.exceptions.JSReflectionException 26 | import org.junit.jupiter.api.* 27 | import kotlin.test.assertEquals 28 | import kotlin.test.assertFailsWith 29 | import kotlin.test.assertNotNull 30 | import kotlin.test.assertNull 31 | 32 | @DisplayName("Reflection Tests") 33 | class ReflectionTests { 34 | @Nested 35 | @DisplayName("Serialization Tests") 36 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 37 | inner class SerializationTests { 38 | private val serializer = JSSerializer { 39 | this.isSafe = true 40 | } 41 | 42 | @BeforeEach fun `Clear Strategies Cache` (info: RepetitionInfo) { 43 | if(info.currentRepetition == info.totalRepetitions) { 44 | serializer.cache.clearStrategies() 45 | } 46 | } 47 | 48 | @RepeatedTest(100) fun `Serialize Class Instance to JSON` () { 49 | val logan = Person("Logan", 66) 50 | val maxine = Person("Maxine", 61) 51 | val james = Person("James", 22, mother = maxine, father = logan) 52 | val house = House("123 Home Address", listOf(logan, maxine, james)) 53 | serializer.serialize(house) 54 | } 55 | } 56 | 57 | @Nested 58 | @DisplayName("Deserialization Tests") 59 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 60 | inner class DeserializationTests { 61 | private val deserializer = JSDeserializer() 62 | 63 | @BeforeEach fun `Clear Strategies Cache` () { 64 | deserializer.cache.clearStrategies() 65 | } 66 | 67 | @RepeatedTest(100) fun `Deserialize JSON to Class Instance` () { 68 | val logan = JSObject { 69 | "name" to "Logan" 70 | "age" to 66 71 | } 72 | 73 | val maxine = JSObject { 74 | "name" to "Maxine" 75 | "age" to 61 76 | } 77 | 78 | val james = JSObject { 79 | "name" to "James" 80 | "age" to 22 81 | "mother" to maxine 82 | "father" to logan 83 | } 84 | 85 | val json = JSObject { 86 | "address" to "123 House Address" 87 | "tenants" to JSArray(james, logan, maxine) 88 | } 89 | 90 | val house = deserializer.deserialize(json) 91 | val person = house.tenants.find { it.name == "James" } 92 | 93 | assertEquals("123 House Address", house.address) 94 | checkPerson("James", 22, person) 95 | checkPerson("Maxine", 61, person?.mother) 96 | checkPerson("Logan", 66, person?.father) 97 | } 98 | 99 | @RepeatedTest(100) fun `Deserialize JSON to Non-Data Class` () { 100 | val json = JSObject { 101 | "name" to "Introduction to Kotlin" 102 | "pages" to 178 103 | "author" to null 104 | } 105 | 106 | val book = deserializer.deserialize(json) 107 | 108 | assertEquals("Introduction to Kotlin", book.name) 109 | assertEquals(178, book.pages) 110 | assertNull(book.author) 111 | 112 | json -= "author" 113 | 114 | assertFailsWith { deserializer.deserialize(json) } 115 | } 116 | 117 | private fun checkPerson(expectedName: String, expectedAge: Int, person: Person?) { 118 | assertNotNull(person) 119 | assertEquals(expectedName, person?.name) 120 | assertEquals(expectedAge, person?.age) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Kaidan Gustave 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | rootProject.name = 'kotlin-json' 17 | 18 | void module(String name, String path = null) { 19 | include name 20 | if(path != null) { 21 | project(name).projectDir = file(path) 22 | } 23 | } 24 | 25 | module ':common' 26 | module ':core' 27 | module ':js' 28 | module ':jdk6' 29 | module ':jdk8' 30 | module ':reflect' 31 | module ':ktor-client', '/libraries/ktor/client' 32 | module ':ktor-common', '/libraries/ktor/common' 33 | module ':ktor-server', '/libraries/ktor/server' --------------------------------------------------------------------------------