From cc08b76b68a66bc8ef094dcaccbee5c561bfabe0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 25 Jul 2025 14:09:06 +0200 Subject: [PATCH] Introduce sop-java-json-gson module --- settings.gradle | 3 +- sop-java-json-gson/README.md | 13 +++ sop-java-json-gson/build.gradle | 28 ++++++ .../src/main/kotlin/sop/GsonParser.kt | 23 +++++ .../src/main/kotlin/sop/GsonSerializer.kt | 16 ++++ .../kotlin/sop/GsonSerializerAndParserTest.kt | 96 +++++++++++++++++++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 sop-java-json-gson/README.md create mode 100644 sop-java-json-gson/build.gradle create mode 100644 sop-java-json-gson/src/main/kotlin/sop/GsonParser.kt create mode 100644 sop-java-json-gson/src/main/kotlin/sop/GsonSerializer.kt create mode 100644 sop-java-json-gson/src/test/kotlin/sop/GsonSerializerAndParserTest.kt diff --git a/settings.gradle b/settings.gradle index 1cb66be..84dc381 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,5 +7,6 @@ rootProject.name = 'SOP-Java' include 'sop-java', 'sop-java-picocli', 'sop-java-testfixtures', - 'external-sop' + 'external-sop', + 'sop-java-json-gson' diff --git a/sop-java-json-gson/README.md b/sop-java-json-gson/README.md new file mode 100644 index 0000000..9feb8ff --- /dev/null +++ b/sop-java-json-gson/README.md @@ -0,0 +1,13 @@ + + +# SOP-Java-JSON-GSON + +## JSON Parsing VERIFICATION extension JSON using Gson + +Since revision 11, the SOP specification defines VERIFICATIONS extension JSON. + +This module implements the `JSONParser` and `JSONSerializer` interfaces using Googles Gson library. diff --git a/sop-java-json-gson/build.gradle b/sop-java-json-gson/build.gradle new file mode 100644 index 0000000..4105902 --- /dev/null +++ b/sop-java-json-gson/build.gradle @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +plugins { + id 'java-library' +} + +group 'org.pgpainless' + +repositories { + mavenCentral() +} + +dependencies { + implementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + implementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" + runtimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + + implementation project(":sop-java") + api "org.slf4j:slf4j-api:$slf4jVersion" + testImplementation "ch.qos.logback:logback-classic:$logbackVersion" + + // @Nonnull, @Nullable... + implementation "com.google.code.findbugs:jsr305:$jsrVersion" + + api "com.google.code.gson:gson:$gsonVersion" +} diff --git a/sop-java-json-gson/src/main/kotlin/sop/GsonParser.kt b/sop-java-json-gson/src/main/kotlin/sop/GsonParser.kt new file mode 100644 index 0000000..06adecb --- /dev/null +++ b/sop-java-json-gson/src/main/kotlin/sop/GsonParser.kt @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop + +import com.google.gson.Gson +import com.google.gson.JsonSyntaxException +import com.google.gson.reflect.TypeToken +import java.text.ParseException + +class GsonParser( + private val gson: Gson = Gson() +) : Verification.JSONParser { + + override fun parse(string: String): Verification.JSON { + try { + return gson.fromJson(string, object : TypeToken(){}.type) + } catch (e: JsonSyntaxException) { + throw ParseException(e.message, 0) + } + } +} \ No newline at end of file diff --git a/sop-java-json-gson/src/main/kotlin/sop/GsonSerializer.kt b/sop-java-json-gson/src/main/kotlin/sop/GsonSerializer.kt new file mode 100644 index 0000000..410fe49 --- /dev/null +++ b/sop-java-json-gson/src/main/kotlin/sop/GsonSerializer.kt @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop + +import com.google.gson.Gson + +class GsonSerializer( + private val gson: Gson = Gson() +) : Verification.JSONSerializer { + + override fun serialize(json: Verification.JSON): String { + return gson.toJson(json) + } +} \ No newline at end of file diff --git a/sop-java-json-gson/src/test/kotlin/sop/GsonSerializerAndParserTest.kt b/sop-java-json-gson/src/test/kotlin/sop/GsonSerializerAndParserTest.kt new file mode 100644 index 0000000..9bbef14 --- /dev/null +++ b/sop-java-json-gson/src/test/kotlin/sop/GsonSerializerAndParserTest.kt @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package sop + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import java.text.ParseException + +class GsonSerializerAndParserTest { + + private val serializer: GsonSerializer = GsonSerializer() + private val parser: GsonParser = GsonParser() + + @Test + fun simpleSingleTest() { + val before = Verification.JSON("/tmp/alice.pgp") + + val json = serializer.serialize(before) + assertEquals("{\"signers\":[\"/tmp/alice.pgp\"]}", json) + + val after = parser.parse(json) + + assertEquals(before, after) + } + + @Test + fun simpleListTest() { + val before = Verification.JSON(listOf("/tmp/alice.pgp", "/tmp/bob.asc")) + + val json = serializer.serialize(before) + assertEquals("{\"signers\":[\"/tmp/alice.pgp\",\"/tmp/bob.asc\"]}", json) + + val after = parser.parse(json) + + assertEquals(before, after) + } + + @Test + fun withCommentTest() { + val before = Verification.JSON( + listOf("/tmp/alice.pgp"), + "This is a comment.", + null) + + val json = serializer.serialize(before) + assertEquals("{\"signers\":[\"/tmp/alice.pgp\"],\"comment\":\"This is a comment.\"}", json) + + val after = parser.parse(json) + + assertEquals(before, after) + } + + @Test + fun withExtStringTest() { + val before = Verification.JSON( + listOf("/tmp/alice.pgp"), + "This is a comment.", + "This is an ext object string.") + + val json = serializer.serialize(before) + assertEquals("{\"signers\":[\"/tmp/alice.pgp\"],\"comment\":\"This is a comment.\",\"ext\":\"This is an ext object string.\"}", json) + + val after = parser.parse(json) + + assertEquals(before, after) + } + + @Test + fun withExtListTest() { + val before = Verification.JSON( + listOf("/tmp/alice.pgp"), + "This is a comment.", + listOf(1.0,2.0,3.0)) + + val json = serializer.serialize(before) + assertEquals("{\"signers\":[\"/tmp/alice.pgp\"],\"comment\":\"This is a comment.\",\"ext\":[1.0,2.0,3.0]}", json) + + val after = parser.parse(json) + + assertEquals(before, after) + } + + @Test + fun parseInvalidJSON() { + assertThrows { parser.parse("Invalid") } + } + + @Test + fun parseMalformedJSON() { + // Missing '}' + assertThrows { parser.parse("{\"signers\":[\"Alice\"]") } + } +} \ No newline at end of file