From 19be0e722f7dd94d42d2a5658161b6b97ffdb961 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:17:36 +0200 Subject: [PATCH 1/8] fix: correct class name extraction in classNamesV3 and classNamesV2 classesV3 and classesV2 are lists of Class<*> objects. Calling .javaClass on a Class<*> returns the metaclass (java.lang.Class), so nameFromJavaClass was receiving java.lang.Class for every entry and producing ["class", "class", ...] instead of the actual type names. Pass the Class<*> directly instead. --- src/main/kotlin/iofXml/Main.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/iofXml/Main.kt b/src/main/kotlin/iofXml/Main.kt index b39e7af8e..4541612ee 100755 --- a/src/main/kotlin/iofXml/Main.kt +++ b/src/main/kotlin/iofXml/Main.kt @@ -72,7 +72,7 @@ val classesV3 = listOf( /** * List of all names for all main classes of IOF V3 XSD specification, generated from [classesV3][classesV3]. */ -val classNamesV3 = classesV3.map { nameFromJavaClass(it.javaClass) } +val classNamesV3 = classesV3.map { nameFromJavaClass(it) } /** * List of all main types / classes of IOF V2 XSD specification. Only these @@ -97,4 +97,4 @@ val classesV2 = listOf( /** * List of all names for all main classes of IOF V2 XSD specification, generated from [classesV2][classesV2]. */ -val classNamesV2 = classesV2.map { nameFromJavaClass(it.javaClass) } +val classNamesV2 = classesV2.map { nameFromJavaClass(it) } From 6c3138392d20581da0073df0d708d769c06ec9e4 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:17:58 +0200 Subject: [PATCH 2/8] fix: throw IllegalArgumentException on XML type mismatch instead of printing to stdout Callers of unmarshalV3Xml and unmarshalV2Xml with a mismatched className would silently continue and produce an incorrectly typed result. Throwing IllegalArgumentException makes the programming error visible immediately and prevents the call from producing a cast exception later. --- src/main/kotlin/iofXml/V2Unmarshallers.kt | 2 +- src/main/kotlin/iofXml/V3Unmarshallers.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/iofXml/V2Unmarshallers.kt b/src/main/kotlin/iofXml/V2Unmarshallers.kt index 9b0395b8d..9b3553358 100644 --- a/src/main/kotlin/iofXml/V2Unmarshallers.kt +++ b/src/main/kotlin/iofXml/V2Unmarshallers.kt @@ -55,7 +55,7 @@ private fun unmarshalV2Xml(className: String, dirtyXml: String): Any { val xml = removeUTF8BOM(dirtyXml, mainElementName) if (mainElementName != className) { - println("ERROR V2: mainElementName=$mainElementName is not equal to className=$className") + throw IllegalArgumentException("V2: mainElementName=$mainElementName is not equal to className=$className") } val actualClass = Class.forName("iofXml.v2.$className") diff --git a/src/main/kotlin/iofXml/V3Unmarshallers.kt b/src/main/kotlin/iofXml/V3Unmarshallers.kt index eb665da6d..07408adc8 100644 --- a/src/main/kotlin/iofXml/V3Unmarshallers.kt +++ b/src/main/kotlin/iofXml/V3Unmarshallers.kt @@ -45,7 +45,7 @@ private fun unmarshalV3Xml(className: String, dirtyXml: String, validateXml: Boo val mainElementName = getMainElementName(dirtyXml) ?: "" val xml = removeUTF8BOM(dirtyXml, mainElementName) if (mainElementName != className) { - println("ERROR V3: mainElementName=$mainElementName is not equal to className=$className") + throw IllegalArgumentException("V3: mainElementName=$mainElementName is not equal to className=$className") } val actualClass = Class.forName("iofXml.v3.$className") From 17a378cdb88cb0f0ae1ed6cbc5eb86b8b94f42e4 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:18:29 +0200 Subject: [PATCH 3/8] fix: replace deprecated ObjectMapper.setSerializationInclusion with JsonMapper builder API ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.13. Using JsonMapper.builder().serializationInclusion() is the recommended replacement and eliminates the compiler deprecation warning. --- src/main/kotlin/iofXml/JsonMarshal.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/iofXml/JsonMarshal.kt b/src/main/kotlin/iofXml/JsonMarshal.kt index b40884679..20b7e9ebd 100644 --- a/src/main/kotlin/iofXml/JsonMarshal.kt +++ b/src/main/kotlin/iofXml/JsonMarshal.kt @@ -3,6 +3,7 @@ package iofXml import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.json.JsonMapper import java.util.Locale import java.util.TimeZone import kotlin.collections.HashMap @@ -103,11 +104,12 @@ internal fun iofJsonToXml(json: String, iofVersion: String = "v3"): String { * @sample iofXml.JsonMarshalKtTest.marshalIofObjectToJson */ fun marshalIofObjectToJson(obj: Any, prettyPrint: Boolean = true): String { - val mapper = ObjectMapper() - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) // Else all fields not set will be 'null' + val builder = JsonMapper.builder() + .serializationInclusion(JsonInclude.Include.NON_NULL) if (prettyPrint) { - mapper.enable(SerializationFeature.INDENT_OUTPUT) + builder.enable(SerializationFeature.INDENT_OUTPUT) } + val mapper = builder.build() //mapper.enable(SerializationFeature.WRAP_ROOT_VALUE) // Problem: root will be UpperCamelCase (need lowerCamelCase) val className = nameFromJavaClass(obj.javaClass) From 560477309d486fc76428e7b9cee64199986d5ffc Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:18:50 +0200 Subject: [PATCH 4/8] style: remove redundant import java.lang.Class from unmarshallers java.lang is automatically available on the JVM platform and does not need to be explicitly imported in Kotlin source files. --- src/main/kotlin/iofXml/V2Unmarshallers.kt | 1 - src/main/kotlin/iofXml/V3Unmarshallers.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/kotlin/iofXml/V2Unmarshallers.kt b/src/main/kotlin/iofXml/V2Unmarshallers.kt index 9b3553358..3dcd0f0fe 100644 --- a/src/main/kotlin/iofXml/V2Unmarshallers.kt +++ b/src/main/kotlin/iofXml/V2Unmarshallers.kt @@ -1,7 +1,6 @@ package iofXml import java.io.StringReader -import java.lang.Class import jakarta.xml.bind.JAXBContext import javax.xml.parsers.SAXParserFactory import javax.xml.transform.sax.SAXSource diff --git a/src/main/kotlin/iofXml/V3Unmarshallers.kt b/src/main/kotlin/iofXml/V3Unmarshallers.kt index 07408adc8..09d6ee0bf 100644 --- a/src/main/kotlin/iofXml/V3Unmarshallers.kt +++ b/src/main/kotlin/iofXml/V3Unmarshallers.kt @@ -1,7 +1,6 @@ package iofXml import java.io.StringReader -import java.lang.Class import java.net.URL import jakarta.xml.bind.JAXBContext import javax.xml.XMLConstants From 260749658114ec8354112d377fb8cdd3f4249968 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:19:10 +0200 Subject: [PATCH 5/8] style: remove Java-style semicolons from V2Unmarshallers.kt Kotlin does not require statement-terminating semicolons. The two occurrences in V2Unmarshallers.kt were leftovers from Java-style code. --- src/main/kotlin/iofXml/V2Unmarshallers.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/iofXml/V2Unmarshallers.kt b/src/main/kotlin/iofXml/V2Unmarshallers.kt index 3dcd0f0fe..1f7820a54 100644 --- a/src/main/kotlin/iofXml/V2Unmarshallers.kt +++ b/src/main/kotlin/iofXml/V2Unmarshallers.kt @@ -34,7 +34,7 @@ fun unmarshalGenericIofV2(xml: String): Triple> { // Credit: https://stackoverflow.com/a/64931583/5550386 val spf = SAXParserFactory.newInstance() // Do not validate DTD - spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) val xmlSource = SAXSource( spf.newSAXParser().xmlReader, InputSource(StringReader(xmlClean)) @@ -68,7 +68,7 @@ private fun unmarshalV2Xml(className: String, dirtyXml: String): Any { // Credit: https://stackoverflow.com/a/64931583/5550386 val spf = SAXParserFactory.newInstance() // Do not validate DTD - spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + spf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) val xmlSource = SAXSource( spf.newSAXParser().xmlReader, InputSource(StringReader(xml)) From fb6942781f39743d50ff76ec3dd7285202bbad85 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:19:31 +0200 Subject: [PATCH 6/8] style: remove commented-out code from JsonMarshal.kt Two commented-out mapper configuration lines were left in iofJsonToXml and marshalIofObjectToJson. These are not conditional experiments but abandoned fragments that add noise without value. --- src/main/kotlin/iofXml/JsonMarshal.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/iofXml/JsonMarshal.kt b/src/main/kotlin/iofXml/JsonMarshal.kt index 20b7e9ebd..8ef1e2f1d 100644 --- a/src/main/kotlin/iofXml/JsonMarshal.kt +++ b/src/main/kotlin/iofXml/JsonMarshal.kt @@ -63,7 +63,6 @@ fun iofV2JsonToXml(json: String) = iofJsonToXml(json, "v2") internal fun iofJsonToXml(json: String, iofVersion: String = "v3"): String { val mapper = ObjectMapper() mapper.setTimeZone(TimeZone.getDefault()) - //mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); val tempJsonMap = mapper.readValue(json, HashMap::class.java) val mainKeys = tempJsonMap.keys @@ -111,7 +110,6 @@ fun marshalIofObjectToJson(obj: Any, prettyPrint: Boolean = true): String { } val mapper = builder.build() - //mapper.enable(SerializationFeature.WRAP_ROOT_VALUE) // Problem: root will be UpperCamelCase (need lowerCamelCase) val className = nameFromJavaClass(obj.javaClass) val objectWithRoot = mapOf(className to obj) From 82dfad9707e71064a203e5b32f1da37ccdafdb52 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:19:47 +0200 Subject: [PATCH 7/8] fix: make main() function non-private so JVM can invoke it as entry point A private top-level fun main() compiles to a private static method on the JVM. The JVM entry point requires a public static void main(String[]) signature, so ./gradlew run would fail silently. Removing the private modifier allows the application plugin to invoke it correctly. --- src/main/kotlin/iofXml/Main.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/iofXml/Main.kt b/src/main/kotlin/iofXml/Main.kt index 4541612ee..b89f71c99 100755 --- a/src/main/kotlin/iofXml/Main.kt +++ b/src/main/kotlin/iofXml/Main.kt @@ -6,7 +6,7 @@ import java.io.StringWriter import jakarta.xml.bind.JAXBContext import jakarta.xml.bind.Marshaller -private fun main() { +fun main() { val file = File("src/test/resources/v2-examples/ResultList_example.xml").readText() val (obj, name, theClass) = unmarshalGenericIofV2(file) println(ObjectMapper().writeValueAsString(obj)) From ebbd86d5dc5e73eb491d493471b0fae3b42e6d43 Mon Sep 17 00:00:00 2001 From: mikaello <2505178+mikaello@users.noreply.github.com> Date: Sat, 4 Apr 2026 18:20:00 +0200 Subject: [PATCH 8/8] style: use isNotEmpty() instead of .length > 0 in test assertion isNotEmpty() is the idiomatic Kotlin way to check that a string is not empty and communicates intent more clearly than comparing length to zero. --- src/test/kotlin/iofXml/V2UnmarshallersKtTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/iofXml/V2UnmarshallersKtTest.kt b/src/test/kotlin/iofXml/V2UnmarshallersKtTest.kt index eec114b50..8455dd42b 100644 --- a/src/test/kotlin/iofXml/V2UnmarshallersKtTest.kt +++ b/src/test/kotlin/iofXml/V2UnmarshallersKtTest.kt @@ -14,7 +14,7 @@ internal class V2UnmarshallersKtTest { val (obj) = unmarshalGenericIofV2(file) val xmlString = marshallIofObject(obj) - assert(xmlString.length > 0) + assert(xmlString.isNotEmpty()) assert(xmlString.contains("")) assert(xmlString.contains("Ejsing")) }