Skip to content

Commit

Permalink
Fixed bug in JSONString, minor changes
Browse files Browse the repository at this point in the history
  • Loading branch information
pwall567 committed Aug 17, 2024
1 parent 03b6699 commit f2cccb2
Show file tree
Hide file tree
Showing 23 changed files with 970 additions and 145 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

The format is based on [Keep a Changelog](http://keepachangelog.com/).

## [9.1] - 2024-08-17
### Changed
- `JSONString`: fixed bug in `toJSON()`
- `JSON`: added `toJSONArray()` and `toJSONObject()` extension functions
- `JSON`, `JSONObject`: added `of()` functions taking vararg array of `JSONObject.Property`
- `JSON`, `JSONObject`: added `DuplicateKeyOption` to functions creating `JSONObject` from `List`
- `JSONArray`, `JSONNumber`, `JSONObject`, `JSONValue`: added pseudo-constructor functions
- `JSONObject`: added `refersTo` infix function to create `JSONObject.Property`
- `JSONArray`, `JSONBoolean`, `JSONDecimal`, `JSONInt`, `JSONLong`, `JSONObject`, `JSONString`, `JSONValue`, `Parser`,
`ParseException`, `AbstractBuilder`: minor code style changes

## [9.0] - 2024-07-24
### Changed
- `JSONArray`, `JSONDecimal`, `JSONInt`, `JSONLong`, `JSONObject`, `JSONString`: minor optimisation to `toJSON()`
Expand Down
48 changes: 41 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ The `JSONValue` interface specifies four functions:

The implementing classes are all immutable.

Following a common Kotlin pattern, there are creation functions named `JSONValue` which create the appropriate
implementing class depending on the parameter type:

| Parameter Type | Result |
|-----------------------------------|-------------------------------|
| `String` | [`JSONString`](#jsonstring) |
| `Int` | [`JSONInt`](#jsonint) |
| `Long` | [`JSONLong`](#jsonlong) |
| `BigDecimal` | [`JSONDecimal`](#jsondecimal) |
| `Boolean` | [`JSONBoolean`](#jsonboolean) |
| `vararg JSONValue?` | [`JSONArray`](#jsonarray) |
| `vararg Pair<String, JSONValue?>` | [`JSONObject`](#jsonobject) |
| `vararg JSONObject.Property` | [`JSONObject`](#jsonobject) |

### `JSONPrimitive`

`JSONPrimitive` is a sealed interface (and a sub-interface of [`JSONValue`](#jsonvalue)) implemented by the classes for
Expand Down Expand Up @@ -107,6 +121,15 @@ types will be regarded as equal.
`JSONInt(27)`, `JSONLong(27)` and `JSONDecimal(27)` will all be considered equal, and will all return the same hash
code.

Following a common Kotlin pattern, there are creation functions named `JSONNumber` which create the appropriate
derived class depending on the parameter type:

| Parameter Type | Result |
|-----------------------------------|-------------------------------|
| `Int` | [`JSONInt`](#jsonint) |
| `Long` | [`JSONLong`](#jsonlong) |
| `BigDecimal` | [`JSONDecimal`](#jsondecimal) |

### `JSONInt`

The `JSONInt` class holds JSON number values that fit in a 32-bit signed integer.
Expand Down Expand Up @@ -206,6 +229,9 @@ The class also implements the [`JSONStructure`](#jsonstructure) interface with a
The constructor for `JSONArray` is not publicly accessible, but an `of()` function is available in the
`companion object`, and a `build` function and the `Builder` nested class allow arrays to be constructed dynamically.

Following a common Kotlin pattern, there is also a creation function named `JSONArray` taking a `vararg` array of
`JSONValue?`.

`JSONArray` implements the `equals()` and `hashCode()` functions as specified for the Java Collections classes, so that
an instance of `JSONArray` may be compared safely with an instance of any class correctly implementing
`List<JSONValue?>`.
Expand Down Expand Up @@ -244,6 +270,9 @@ and:
The constructor for `JSONObject` is not publicly accessible, but an `of()` function is available in the
`companion object`, and a `build` function and the `Builder` nested class allow objects to be constructed dynamically.

Following a common Kotlin pattern, there are also creation functions named `JSONObject` taking a `vararg` array of
`Pair<String, JSONValue?>` or `JSONObject.Property`.

`JSONObject` implements the `equals()` and `hashCode()` functions as specified for the Java Collections classes, so that
an instance of `JSONObject` may be compared safely with an instance of any class correctly implementing
`Map<String, JSONValue?>`.
Expand Down Expand Up @@ -281,6 +310,11 @@ It has two properties:

The `JSONObject.Property` object is immutable.

There is an infix function `refersTo` taking a `String` and a `JSONValue?` which creates a `JSONObject.Property`:
```kotlin
val property = "propertyName" refersTo JSONString("Property value")
```

### `JSONException`

Error conditions will usually result in a `JSONException` being thrown.
Expand Down Expand Up @@ -600,7 +634,7 @@ parser leniency.
For example:
```kotlin
val options = ParseOptions(
objectKeyDuplicate = ParseOptions.DuplicateKeyOption.ERROR,
objectKeyDuplicate = JSONObject.DuplicateKeyOption.ERROR,
objectKeyUnquoted = false,
objectTrailingComma = false,
arrayTrailingComma = false,
Expand All @@ -619,7 +653,7 @@ Under normal circumstances, the parser will throw an exception when it encounter
but if such data is required to be accepted, the `objectKeyDuplicate` options setting may be used to specify the desired
behaviour.

The field is an `enum` (`DuplicateKeyOption`), and the possible values are:
The field is an `enum` (`JSONObject.DuplicateKeyOption`), and the possible values are:

- `ERROR`: treat the duplicate key as an error (this is the default)
- `TAKE_FIRST`: take the value of the first occurrence and ignore duplicates
Expand Down Expand Up @@ -660,25 +694,25 @@ The diagram was produced by [Dia](https://wiki.gnome.org/Apps/Dia/); the diagram

## Dependency Specification

The latest version of the library is 9.0, and it may be obtained from the Maven Central repository.
The latest version of the library is 9.1, and it may be obtained from the Maven Central repository.

### Maven
```xml
<dependency>
<groupId>io.kjson</groupId>
<artifactId>kjson-core</artifactId>
<version>9.0</version>
<version>9.1</version>
</dependency>
```
### Gradle
```groovy
implementation "io.kjson:kjson-core:9.0"
implementation "io.kjson:kjson-core:9.1"
```
### Gradle (kts)
```kotlin
implementation("io.kjson:kjson-core:9.0")
implementation("io.kjson:kjson-core:9.1")
```

Peter Wall

2024-07-24
2024-08-17
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>

<artifactId>kjson-core</artifactId>
<version>9.0</version>
<version>9.1</version>
<name>JSON Kotlin core functionality</name>
<description>JSON Kotlin core functionality</description>
<packaging>jar</packaging>
Expand Down
36 changes: 35 additions & 1 deletion src/main/kotlin/io/kjson/JSON.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package io.kjson
import java.math.BigDecimal
import java.util.function.IntConsumer

import io.kjson.JSONObject.DuplicateKeyOption
import io.kjson.parser.Parser
import io.kjson.parser.ParseOptions
import io.kjson.util.getIntProperty
Expand Down Expand Up @@ -83,10 +84,15 @@ object JSON {
fun of(vararg items: JSONValue?): JSONArray = JSONArray.of(*items)

/**
* Create a [JSONArray] from a `vararg` list of [Pair] of name to value pairs.
* Create a [JSONObject] from a `vararg` list of [Pair] of name to value pairs.
*/
fun of(vararg items: Pair<String, JSONValue?>): JSONObject = JSONObject.of(*items)

/**
* Create a [JSONObject] from a `vararg` list of [JSONObject.Property]s.
*/
fun of(vararg items: JSONObject.Property): JSONObject = JSONObject.of(*items)

/**
* Parse a [String] to a [JSONValue] (or `null` if the string is `"null"`).
*/
Expand Down Expand Up @@ -625,3 +631,31 @@ object JSON {
}

}

// NOTE: extension functions are being added here, rather than in the body of the JSON object, to simplify imports
// More functions may be moved here from the JSON object later.

/**
* Convert a [List] of [JSONValue] items to a [JSONArray].
*/
fun List<JSONValue?>.toJSONArray(): JSONArray = if (isEmpty()) JSONArray.EMPTY else JSONArray(toTypedArray(), size)

/**
* Convert a [Map] to a [JSONObject].
*/
fun Map<String, JSONValue?>.toJSONObject(): JSONObject = if (isEmpty()) JSONObject.EMPTY else
JSONObject.build(size = size) {
for (entry in entries)
add(entry.key, entry.value)
}

/**
* Convert a [List] of [JSONObject.Property]s to a [JSONObject].
*/
fun List<JSONObject.Property>.toJSONObject(
duplicateKeyOption: DuplicateKeyOption = DuplicateKeyOption.ERROR,
): JSONObject = if (isEmpty()) JSONObject.EMPTY else
JSONObject.build(size = size, duplicateKeyOption = duplicateKeyOption) {
for (property in this@toJSONObject)
add(property)
}
11 changes: 7 additions & 4 deletions src/main/kotlin/io/kjson/JSONArray.kt
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,11 @@ class JSONArray internal constructor (private val array: Array<out JSONValue?>,

/** The value as a [JSONArray] (unnecessary when type is known statically). */
@Deprecated("Unnecessary (value is known to be JSONArray)", ReplaceWith("this"))
val asArray: JSONArray
get() = this
val asArray: JSONArray get() = this

/** The value as a [JSONArray] or `null` (unnecessary when type is known statically). */
@Deprecated("Unnecessary (value is known to be JSONArray)", ReplaceWith("this"))
val asArrayOrNull: JSONArray
get() = this
val asArrayOrNull: JSONArray get() = this

companion object {

Expand Down Expand Up @@ -378,3 +376,8 @@ class JSONArray internal constructor (private val array: Array<out JSONValue?>,
}

}

/**
* Create a [JSONArray] from a `vararg` list of [JSONValue]s.
*/
fun JSONArray(vararg items: JSONValue?): JSONArray = JSONArray.of(*items)
6 changes: 2 additions & 4 deletions src/main/kotlin/io/kjson/JSONBoolean.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,11 @@ enum class JSONBoolean(override val value: Boolean) : JSONPrimitive<Boolean> {
override fun toString(): String = toJSON()

/** The value as a [Boolean] (optimisation of the extension value in [JSON] when the type is known statically). */
val asBoolean: Boolean
get() = value
val asBoolean: Boolean get() = value

/** The value as a [Boolean] or `null` (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asBooleanOrNull: Boolean
get() = value
val asBooleanOrNull: Boolean get() = value

companion object {

Expand Down
6 changes: 2 additions & 4 deletions src/main/kotlin/io/kjson/JSONDecimal.kt
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,11 @@ class JSONDecimal(override val value: BigDecimal) : JSONNumber(), JSONPrimitive<

/** The value as a [BigDecimal] (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asDecimal: BigDecimal
get() = value
val asDecimal: BigDecimal get() = value

/** The value as a [BigDecimal] or `null` (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asDecimalOrNull: BigDecimal
get() = value
val asDecimalOrNull: BigDecimal get() = value

companion object {

Expand Down
38 changes: 21 additions & 17 deletions src/main/kotlin/io/kjson/JSONInt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ class JSONInt(override val value: Int) : JSONNumber(), JSONPrimitive<Int> {
/**
* Append as a JSON string to an [Appendable].
*/
override fun appendTo(a: Appendable) = IntOutput.appendInt(a, value)
override fun appendTo(a: Appendable) {
IntOutput.appendInt(a, value)
}

/**
* Convert to a JSON string.
Expand All @@ -57,24 +59,32 @@ class JSONInt(override val value: Int) : JSONNumber(), JSONPrimitive<Int> {
/**
* Output as a JSON string to an [IntConsumer].
*/
override fun outputTo(out: IntConsumer) = IntOutput.outputInt(value, out)
override fun outputTo(out: IntConsumer) {
IntOutput.outputInt(value, out)
}

/**
* Output as a JSON string to an [IntConsumer].
*/
@Deprecated("renamed to outputTo", ReplaceWith("outputTo(out)"))
override fun output(out: IntConsumer) = IntOutput.outputInt(value, out)
override fun output(out: IntConsumer) {
IntOutput.outputInt(value, out)
}

/**
* Output as a JSON string to a [CoOutput].
*/
override suspend fun coOutputTo(out: CoOutput) = out.outputInt(value)
override suspend fun coOutputTo(out: CoOutput) {
out.outputInt(value)
}

/**
* Output as a JSON string to a [CoOutput].
*/
@Deprecated("renamed to coOutputTo", ReplaceWith("coOutputTo(out)"))
override suspend fun coOutput(out: CoOutput) = out.outputInt(value)
override suspend fun coOutput(out: CoOutput) {
out.outputInt(value)
}

/**
* Return `true` if the value is integral (has no fractional part, or the fractional part is zero).
Expand Down Expand Up @@ -237,32 +247,26 @@ class JSONInt(override val value: Int) : JSONNumber(), JSONPrimitive<Int> {
override fun toString(): String = value.toString()

/** The value as an [Int] (optimisation of the extension value in [JSON] when the type is known statically). */
val asInt: Int
get() = value
val asInt: Int get() = value

/** The value as an [Int] or `null` (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asIntOrNull: Int
get() = value
val asIntOrNull: Int get() = value

/** The value as a [Long] (optimisation of the extension value in [JSON] when the type is known statically). */
val asLong: Long
get() = value.toLong()
val asLong: Long get() = value.toLong()

/** The value as a [Long] or `null` (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asLongOrNull: Long
get() = value.toLong()
val asLongOrNull: Long get() = value.toLong()

/** The value as a [BigDecimal] (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asDecimal: BigDecimal
get() = value.toBigDecimal()
val asDecimal: BigDecimal get() = value.toBigDecimal()

/** The value as a [BigDecimal] or `null` (optimisation of the extension value in [JSON] when the type is known
* statically). */
val asDecimalOrNull: BigDecimal
get() = value.toBigDecimal()
val asDecimalOrNull: BigDecimal get() = value.toBigDecimal()

companion object {

Expand Down
Loading

0 comments on commit f2cccb2

Please sign in to comment.