Skip to content

Commit caa994e

Browse files
authored
Merge pull request #2004 from OneSignal/fix/json-ext-functions
Fix JSON Kotlin Extension Functions
2 parents 68a2767 + 2f7f3dd commit caa994e

File tree

8 files changed

+170
-13
lines changed

8 files changed

+170
-13
lines changed

OneSignalSDK/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ buildscript {
1818
ktlintPluginVersion = '11.6.1'
1919
ktlintVersion = '1.0.1'
2020
junitVersion = '4.13.2'
21+
// DO NOT upgrade for tests, using an old version so it matches AOSP
22+
tdunningJsonForTest = '1.0'
2123
}
2224

2325
repositories {

OneSignalSDK/onesignal/core/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ dependencies {
8888
testImplementation("androidx.test:core-ktx:1.4.0")
8989
testImplementation("androidx.test:core:1.4.0")
9090
testImplementation("io.mockk:mockk:1.13.2")
91-
testImplementation("org.json:json:20180813")
9291
testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
92+
93+
// com.tdunning:json is needed for non-Robolectric tests.
94+
testImplementation("com.tdunning:json:$tdunningJsonForTest")
9395
}
9496

9597
ktlint {
@@ -99,4 +101,4 @@ ktlint {
99101
]
100102
}
101103

102-
apply from: '../maven-push.gradle'
104+
apply from: '../maven-push.gradle'

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/JSONObjectExtensions.kt

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.onesignal.common
22

33
import org.json.JSONArray
44
import org.json.JSONObject
5+
import org.json.JSONObject.NULL
56

67
/**
78
* Retrieve an [Int] from the [JSONObject] safely.
@@ -95,15 +96,46 @@ fun JSONObject.safeJSONObject(name: String): JSONObject? {
9596

9697
/**
9798
* Create a [Map] from the [JSONObject].
99+
* Supports nested JSONObjects and JSONArrays.
98100
*/
99-
fun JSONObject.toMap(): Map<String, Any> {
100-
val map = mutableMapOf<String, Any>()
101-
101+
fun JSONObject.toMap(): MutableMap<String, Any?> {
102+
val results: MutableMap<String, Any?> = HashMap()
102103
for (key in this.keys()) {
103-
map[key] = this[key]
104+
val value = this[key]
105+
results[key] =
106+
if (NULL.equals(value)) {
107+
null
108+
} else if (value is JSONObject) {
109+
value.toMap()
110+
} else if (value is JSONArray) {
111+
value.toList()
112+
} else {
113+
value
114+
}
104115
}
116+
return results
117+
}
105118

106-
return map
119+
/**
120+
* Create a [List] from the [JSONArray].
121+
* Supports nested JSONObjects and JSONArrays.
122+
*/
123+
fun JSONArray.toList(): List<Any?>? {
124+
val size = this.length()
125+
val results: MutableList<Any?> = ArrayList(size)
126+
for (i in 0 until size) {
127+
val element = this.opt(i)
128+
if (NULL.equals(element)) {
129+
results.add(null)
130+
} else if (element is JSONArray) {
131+
results.add(element.toList())
132+
} else if (element is JSONObject) {
133+
results.add(element.toMap())
134+
} else {
135+
results.add(element)
136+
}
137+
}
138+
return results
107139
}
108140

109141
/**
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.onesignal.common
2+
3+
import io.kotest.core.spec.style.DescribeSpec
4+
import io.kotest.matchers.shouldBe
5+
import io.kotest.runner.junit4.KotestTestRunner
6+
import io.mockk.mockkStatic
7+
import io.mockk.verify
8+
import org.json.JSONArray
9+
import org.json.JSONObject
10+
import org.junit.runner.RunWith
11+
12+
@RunWith(KotestTestRunner::class)
13+
class JSONObjectExtensionsTest : DescribeSpec({
14+
describe("toMap") {
15+
// Some org.json JVM libraries define their own toMap. We want to
16+
// ensure we are testing ours, not theirs.
17+
it("is using our extension function") {
18+
mockkStatic(JSONObject::toMap) {
19+
JSONObject().toMap()
20+
verify { any<JSONObject>().toMap() }
21+
}
22+
}
23+
24+
it("supports primitives") {
25+
val test =
26+
JSONObject()
27+
.put("String", "String")
28+
.put("Boolean", true)
29+
.put("Integer", 1)
30+
.put("Long", 2L)
31+
.put("Float", 3.3f)
32+
.put("Double", Double.MAX_VALUE)
33+
34+
test.toMap() shouldBe
35+
mapOf(
36+
"String" to "String",
37+
"Boolean" to true,
38+
"Integer" to 1,
39+
"Long" to 2L,
40+
"Float" to 3.3f,
41+
"Double" to Double.MAX_VALUE,
42+
)
43+
}
44+
45+
describe("JSONArray") {
46+
it("supports empty") {
47+
val test = JSONObject().put("MyArray", JSONArray())
48+
test.toMap() shouldBe
49+
mapOf("MyArray" to emptyList<Any>())
50+
}
51+
it("supports one item") {
52+
val test = JSONObject().put("MyArray", JSONArray().put("String"))
53+
test.toMap() shouldBe
54+
mapOf("MyArray" to listOf("String"))
55+
}
56+
}
57+
58+
it("supports JSONObject") {
59+
val test =
60+
JSONObject()
61+
.put("MyNestedJSONObject", JSONObject().put("String", "String"))
62+
test.toMap() shouldBe
63+
mapOf(
64+
"MyNestedJSONObject" to mapOf("String" to "String"),
65+
)
66+
}
67+
}
68+
})
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.onesignal.selftest
2+
3+
import com.onesignal.common.toMap
4+
import com.onesignal.testhelpers.extensions.RobolectricTest
5+
import io.kotest.core.spec.style.FunSpec
6+
import io.kotest.core.spec.style.funSpec
7+
import io.kotest.matchers.shouldBe
8+
import io.kotest.runner.junit4.KotestTestRunner
9+
import org.json.JSONObject
10+
import org.junit.runner.RunWith
11+
12+
/**
13+
* This purpose of this file is to ensure org.json classes being used in
14+
* other tests matches real Android devices.
15+
*/
16+
17+
val keyAOSPBehavior =
18+
funSpec {
19+
// This is a key oddity only AOSP seems to do
20+
test("JSONObject.getString stringifies JSON object") {
21+
val json =
22+
JSONObject()
23+
.put(
24+
"obj",
25+
JSONObject().put("nested", "value"),
26+
)
27+
json.getString("obj") shouldBe "{\"nested\":\"value\"}"
28+
}
29+
}
30+
31+
@RobolectricTest
32+
@RunWith(KotestTestRunner::class)
33+
class JSONObjectRobolectricEnvTest : FunSpec({
34+
test("ensure our src JSON Kotlin extension methods work with @RobolectricTest") {
35+
val test = JSONObject()
36+
test.put("test", "test")
37+
// Ensuring this doesn't throw
38+
test.toMap()
39+
}
40+
41+
include(keyAOSPBehavior)
42+
})
43+
44+
@RunWith(KotestTestRunner::class)
45+
class JSONObjectJVMEnvTest : FunSpec({
46+
include(keyAOSPBehavior)
47+
})

OneSignalSDK/onesignal/in-app-messages/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,10 @@ dependencies {
8888
testImplementation("androidx.test:core-ktx:1.4.0")
8989
testImplementation("androidx.test:core:1.4.0")
9090
testImplementation("io.mockk:mockk:1.13.2")
91-
testImplementation("org.json:json:20180813")
9291
testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
92+
93+
// com.tdunning:json is needed for non-Robolectric tests.
94+
testImplementation("com.tdunning:json:$tdunningJsonForTest")
9395
}
9496

9597
ktlint {
@@ -99,4 +101,4 @@ ktlint {
99101
]
100102
}
101103

102-
apply from: '../maven-push.gradle'
104+
apply from: '../maven-push.gradle'

OneSignalSDK/onesignal/location/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ dependencies {
8787
testImplementation("androidx.test:core-ktx:1.4.0")
8888
testImplementation("androidx.test:core:1.4.0")
8989
testImplementation("io.mockk:mockk:1.13.2")
90-
testImplementation("org.json:json:20180813")
9190
testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
9291
testImplementation("com.google.android.gms:play-services-location:18.0.0")
92+
93+
// com.tdunning:json is needed for non-Robolectric tests.
94+
testImplementation("com.tdunning:json:$tdunningJsonForTest")
9395
}
9496

9597
ktlint {
@@ -99,4 +101,4 @@ ktlint {
99101
]
100102
}
101103

102-
apply from: '../maven-push.gradle'
104+
apply from: '../maven-push.gradle'

OneSignalSDK/onesignal/notifications/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ dependencies {
100100
testImplementation("androidx.test:core-ktx:1.4.0")
101101
testImplementation("androidx.test:core:1.4.0")
102102
testImplementation("io.mockk:mockk:1.13.2")
103-
testImplementation("org.json:json:20180813")
104103
testImplementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
104+
105+
// com.tdunning:json is needed for non-Robolectric tests.
106+
testImplementation("com.tdunning:json:$tdunningJsonForTest")
105107
}
106108

107109
ktlint {
@@ -111,4 +113,4 @@ ktlint {
111113
]
112114
}
113115

114-
apply from: '../maven-push.gradle'
116+
apply from: '../maven-push.gradle'

0 commit comments

Comments
 (0)