All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dorkbox.json.JsonWriter.kt Maven / Gradle / Ivy

/*
 * Copyright 2023 dorkbox, llc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*******************************************************************************
 * Copyright 2011 Mario Zechner, Nathan Sweet
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dorkbox.json

import java.io.IOException
import java.io.Writer
import java.math.BigDecimal
import java.math.BigInteger

/**
 * Builder style API for emitting JSON.
 *
 * @author Nathan Sweet
 */
class JsonWriter(val writer: Writer) : Writer() {

    private val stack = ArrayList()
    private var current: JsonObject? = null
    private var named = false
    private var outputType = OutputType.json
    private var quoteLongValues = false

    /** Sets the type of JSON output. Default is [OutputType.minimal].  */
    fun setOutputType(outputType: OutputType) {
        this.outputType = outputType
    }

    /**
     * When true, quotes long, double, BigInteger, BigDecimal types to prevent truncation in languages like JavaScript and PHP.
     * This is not necessary when using libgdx, which handles these types without truncation. Default is false.
     */
    fun setQuoteLongValues(quoteLongValues: Boolean) {
        this.quoteLongValues = quoteLongValues
    }

    @Throws(IOException::class)
    fun name(name: String): JsonWriter {
        check(!(current == null || current!!.array)) { "Current item must be an object." }

        if (!current!!.needsComma) {
            current!!.needsComma = true
        } else {
            writer.write(','.code)
        }

        writer.write(outputType.quoteName(name))
        writer.write(':'.code)
        named = true
        return this
    }

    @Throws(IOException::class)
    fun `object`(): JsonWriter {
        requireCommaOrName()
        stack.add(JsonObject(false).also { current = it })
        return this
    }

    @Throws(IOException::class)
    fun array(): JsonWriter {
        requireCommaOrName()
        stack.add(JsonObject(true).also { current = it })
        return this
    }

    @Throws(IOException::class)
    fun value(value: Any?): JsonWriter {
        @Suppress("NAME_SHADOWING")
        var value = value

        if (quoteLongValues && (value is Long || value is Double || value is BigDecimal || value is BigInteger)) {
            value = value.toString()
        } else if (value is Number) {
            val number = value
            val longValue = number.toLong()

            if (number.toDouble() == longValue.toDouble()) {
                value = longValue
            }
        }
        requireCommaOrName()
        writer.write(outputType.quoteValue(value))
        return this
    }

    /** Writes the specified JSON value, without quoting or escaping.  */
    @Throws(IOException::class)
    fun json(json: String): JsonWriter {
        requireCommaOrName()
        writer.write(json)
        return this
    }

    @Throws(IOException::class)
    private fun requireCommaOrName() {
        if (current == null) return

        if (current!!.array) {
            if (!current!!.needsComma) {
                current!!.needsComma = true
            } else {
                writer.write(','.code)
            }
        } else {
            check(named) { "Name must be set." }
            named = false
        }
    }

    @Throws(IOException::class)
    fun `object`(name: String): JsonWriter {
        return name(name).`object`()
    }

    @Throws(IOException::class)
    fun array(name: String): JsonWriter {
        return name(name).array()
    }

    @Throws(IOException::class)
    operator fun set(name: String, value: Any): JsonWriter {
        return name(name).value(value)
    }

    /** Writes the specified JSON value, without quoting or escaping.  */
    @Throws(IOException::class)
    fun json(name: String, json: String): JsonWriter {
        return name(name).json(json)
    }

    @Throws(IOException::class)
    fun pop(): JsonWriter {
        check(!named) { "Expected an object, array, or value since a name was set." }

        stack.removeAt(stack.size - 1).close()
        current = if (stack.size == 0) {
            null
        } else {
            stack[stack.size - 1]
        }
        return this
    }

    @Throws(IOException::class)
    override fun write(cbuf: CharArray, off: Int, len: Int) {
        writer.write(cbuf, off, len)
    }

    @Throws(IOException::class)
    override fun flush() {
        writer.flush()
    }

    @Throws(IOException::class)
    override fun close() {
        while (stack.size > 0) pop()
        writer.close()
    }

    private inner class JsonObject(val array: Boolean) {
        var needsComma = false

        init {
            writer.write((if (array) '[' else '{').code)
        }

        @Throws(IOException::class)
        fun close() {
            writer.write((if (array) ']' else '}').code)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy