Skip to content

Commit e0a7c2f

Browse files
authored
Merge pull request #39 from hossain-khan/copilot/fix-38
Improve JSON5 parser API with strongly-typed JSON5Value sealed class
2 parents f9584c9 + b04fb4a commit e0a7c2f

File tree

5 files changed

+476
-141
lines changed

5 files changed

+476
-141
lines changed

lib/src/main/kotlin/dev/hossain/json5kt/JSON5.kt

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,50 @@ object JSON5 {
1414
private val format = DefaultJSON5Format
1515

1616
/**
17-
* Parses a JSON5 string into a Kotlin object.
17+
* Parses a JSON5 string into a strongly-typed JSON5Value.
18+
*
19+
* @param text JSON5 text to parse
20+
* @return The parsed value as a JSON5Value
21+
* @throws JSON5Exception if the input is invalid JSON5
22+
*/
23+
fun parse(text: String): JSON5Value {
24+
val result = JSON5Parser.parse(text)
25+
return JSON5Value.from(result)
26+
}
27+
28+
/**
29+
* Parses a JSON5 string into a raw Kotlin object without type conversion.
1830
*
1931
* @param text JSON5 text to parse
2032
* @return The parsed value (Map, List, String, Number, Boolean, or null)
2133
* @throws JSON5Exception if the input is invalid JSON5
2234
*/
23-
fun parse(text: String): Any? {
35+
fun parseToAny(text: String): Any? {
2436
return JSON5Parser.parse(text)
2537
}
2638

2739
/**
28-
* Parses a JSON5 string into a Kotlin object, with a reviver function.
40+
* Parses a JSON5 string into a strongly-typed JSON5Value, with a reviver function.
41+
*
42+
* @param text JSON5 text to parse
43+
* @param reviver A function that transforms the parsed values
44+
* @return The parsed value as a JSON5Value
45+
* @throws JSON5Exception if the input is invalid JSON5
46+
*/
47+
fun parse(text: String, reviver: (key: String, value: Any?) -> Any?): JSON5Value {
48+
val result = JSON5Parser.parse(text, reviver)
49+
return JSON5Value.from(result)
50+
}
51+
52+
/**
53+
* Parses a JSON5 string into a raw Kotlin object without type conversion, with a reviver function.
2954
*
3055
* @param text JSON5 text to parse
3156
* @param reviver A function that transforms the parsed values
3257
* @return The parsed value (Map, List, String, Number, Boolean, or null)
3358
* @throws JSON5Exception if the input is invalid JSON5
3459
*/
35-
fun parse(text: String, reviver: (key: String, value: Any?) -> Any?): Any? {
60+
fun parseToAny(text: String, reviver: (key: String, value: Any?) -> Any?): Any? {
3661
return JSON5Parser.parse(text, reviver)
3762
}
3863

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package dev.hossain.json5kt
2+
3+
/**
4+
* Represents a JSON5 value as described in the JSON5 specification.
5+
* JSON5 values can be objects, arrays, strings, numbers, booleans, or null.
6+
*
7+
* @see <a href="https://spec.json5.org/">JSON5 Specification</a>
8+
*/
9+
sealed class JSON5Value {
10+
11+
/**
12+
* Represents a JSON5 object, which is a collection of name/value pairs.
13+
*
14+
* @property value The map of property names to JSON5 values
15+
* @see <a href="https://spec.json5.org/#objects">JSON5 Objects</a>
16+
*/
17+
data class Object(val value: Map<kotlin.String, JSON5Value>) : JSON5Value() {
18+
override fun toString(): kotlin.String = value.toString()
19+
}
20+
21+
/**
22+
* Represents a JSON5 array, which is an ordered sequence of JSON5 values.
23+
*
24+
* @property value The list of JSON5 values
25+
* @see <a href="https://spec.json5.org/#arrays">JSON5 Arrays</a>
26+
*/
27+
data class Array(val value: List<JSON5Value>) : JSON5Value() {
28+
override fun toString(): kotlin.String = value.toString()
29+
}
30+
31+
/**
32+
* Represents a JSON5 string value.
33+
*
34+
* @property value The string value
35+
* @see <a href="https://spec.json5.org/#strings">JSON5 Strings</a>
36+
*/
37+
data class String(val value: kotlin.String) : JSON5Value() {
38+
override fun toString(): kotlin.String = "\"$value\""
39+
}
40+
41+
/**
42+
* Represents a JSON5 number value, which can be an integer, float, hex, infinity, or NaN.
43+
*
44+
* @see <a href="https://spec.json5.org/#numbers">JSON5 Numbers</a>
45+
*/
46+
sealed class Number : JSON5Value() {
47+
/**
48+
* Represents an integer number in JSON5.
49+
*
50+
* @property value The integer value
51+
*/
52+
data class Integer(val value: Long) : Number() {
53+
override fun toString(): kotlin.String = value.toString()
54+
}
55+
56+
/**
57+
* Represents a decimal number in JSON5.
58+
*
59+
* @property value The decimal value
60+
*/
61+
data class Decimal(val value: Double) : Number() {
62+
override fun toString(): kotlin.String = value.toString()
63+
}
64+
65+
/**
66+
* Represents a hexadecimal number in JSON5 (0x prefix).
67+
*
68+
* @property value The integer value represented by the hex
69+
*/
70+
data class Hexadecimal(val value: Long) : Number() {
71+
override fun toString(): kotlin.String = "0x${value.toString(16)}"
72+
}
73+
74+
/**
75+
* Represents positive infinity in JSON5.
76+
*/
77+
object PositiveInfinity : Number() {
78+
override fun toString(): kotlin.String = "Infinity"
79+
}
80+
81+
/**
82+
* Represents negative infinity in JSON5.
83+
*/
84+
object NegativeInfinity : Number() {
85+
override fun toString(): kotlin.String = "-Infinity"
86+
}
87+
88+
/**
89+
* Represents NaN (Not a Number) in JSON5.
90+
*/
91+
object NaN : Number() {
92+
override fun toString(): kotlin.String = "NaN"
93+
}
94+
}
95+
96+
/**
97+
* Represents a JSON5 boolean value.
98+
*
99+
* @property value The boolean value
100+
* @see <a href="https://spec.json5.org/#primitives">JSON5 Primitives</a>
101+
*/
102+
data class Boolean(val value: kotlin.Boolean) : JSON5Value() {
103+
override fun toString(): kotlin.String = value.toString()
104+
}
105+
106+
/**
107+
* Represents a JSON5 null value.
108+
*
109+
* @see <a href="https://spec.json5.org/#primitives">JSON5 Primitives</a>
110+
*/
111+
object Null : JSON5Value() {
112+
override fun toString(): kotlin.String = "null"
113+
}
114+
115+
/**
116+
* Helper methods to convert primitive Kotlin types to JSON5Value objects.
117+
*/
118+
companion object {
119+
/**
120+
* Creates a JSON5Value from a Kotlin object.
121+
*
122+
* @param value The Kotlin object to convert
123+
* @return The corresponding JSON5Value
124+
* @throws IllegalArgumentException if the value type is not supported
125+
*/
126+
fun from(value: Any?): JSON5Value {
127+
return when (value) {
128+
null -> Null
129+
is Map<*, *> -> {
130+
val jsonMap = mutableMapOf<kotlin.String, JSON5Value>()
131+
value.forEach { (k, v) ->
132+
if (k is kotlin.String) {
133+
jsonMap[k] = from(v)
134+
}
135+
}
136+
Object(jsonMap)
137+
}
138+
is List<*> -> {
139+
val jsonList = value.map { from(it) }
140+
Array(jsonList)
141+
}
142+
is kotlin.String -> String(value)
143+
is Int -> Number.Integer(value.toLong())
144+
is Long -> Number.Integer(value)
145+
is Float -> Number.Decimal(value.toDouble())
146+
is Double -> {
147+
when {
148+
value.isNaN() -> Number.NaN
149+
value.isInfinite() && value > 0 -> Number.PositiveInfinity
150+
value.isInfinite() && value < 0 -> Number.NegativeInfinity
151+
else -> Number.Decimal(value)
152+
}
153+
}
154+
is kotlin.Boolean -> Boolean(value)
155+
else -> throw IllegalArgumentException("Unsupported type: ${value::class.java}")
156+
}
157+
}
158+
}
159+
}

0 commit comments

Comments
 (0)