From a645a2ec53b02757fd851ee8935bff490a0d9618 Mon Sep 17 00:00:00 2001 From: iroqueta Date: Wed, 21 May 2025 16:31:45 -0300 Subject: [PATCH 1/2] Fix FromJson and ToJson methods performance caused by json lib changes --- common/pom.xml | 2 +- .../com/genexus/json/JSONObjectWrapper.java | 3 +- .../com/genexus/json/JSONTokenerWrapper.java | 23 ++ .../com/genexus/xml/GXXMLSerializable.java | 4 +- .../genexus/xml/GXXMLSerializableTest.java | 226 ++++++++++++++++++ 5 files changed, 253 insertions(+), 5 deletions(-) create mode 100644 java/src/test/java/com/genexus/xml/GXXMLSerializableTest.java diff --git a/common/pom.xml b/common/pom.xml index 03af0b6b2..920640fd2 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -53,7 +53,7 @@ org.json json - 20231013 + 20250517 diff --git a/common/src/main/java/com/genexus/json/JSONObjectWrapper.java b/common/src/main/java/com/genexus/json/JSONObjectWrapper.java index d17b9fa6f..fe33bcb70 100644 --- a/common/src/main/java/com/genexus/json/JSONObjectWrapper.java +++ b/common/src/main/java/com/genexus/json/JSONObjectWrapper.java @@ -44,8 +44,7 @@ public JSONObjectWrapper(Map m) { } public JSONObjectWrapper(JSONObject jsonObject) { - super(jsonObject.toString()); - initMap(); + this(jsonObject.toMap()); } private void initMap() { diff --git a/common/src/main/java/com/genexus/json/JSONTokenerWrapper.java b/common/src/main/java/com/genexus/json/JSONTokenerWrapper.java index 6094ee009..6db622119 100644 --- a/common/src/main/java/com/genexus/json/JSONTokenerWrapper.java +++ b/common/src/main/java/com/genexus/json/JSONTokenerWrapper.java @@ -4,9 +4,32 @@ import org.json.JSONException; public class JSONTokenerWrapper extends JSONTokener{ + private String mySource; + private int myIndex; public JSONTokenerWrapper(String string) { super(string); + mySource = string; + myIndex = 0; + } + + public void back() { + if (myIndex > 0) { + myIndex -= 1; + } + } + + public boolean more() { + return myIndex < mySource.length(); + } + + public char next() { + if (more()) { + char c = mySource.charAt(myIndex); + this.myIndex += 1; + return c; + } + return 0; } public Object nextValue() throws JSONException { diff --git a/common/src/main/java/com/genexus/xml/GXXMLSerializable.java b/common/src/main/java/com/genexus/xml/GXXMLSerializable.java index bce82706e..649ac1789 100644 --- a/common/src/main/java/com/genexus/xml/GXXMLSerializable.java +++ b/common/src/main/java/com/genexus/xml/GXXMLSerializable.java @@ -363,7 +363,7 @@ public void FromJSONObject(Object obj) } else { - if (obj instanceof JSONObject) + if ((obj instanceof JSONObject) && !(obj instanceof JSONObjectWrapper)) obj = new JSONObjectWrapper((JSONObject)obj); Iterator objIterator = getJSONObjectIterator((JSONObjectWrapper) obj); Iterator modifiedObjIterator = getFromJSONObjectOrderIterator(objIterator); @@ -467,7 +467,7 @@ private void collectionFromJSONArray(JSONArray jsonArray, GXSimpleCollection gxC for(int i = 0; i < jsonArray.length(); i++) { Object currObj = jsonArray.get(i); - if (currObj instanceof JSONObject) + if ((currObj instanceof JSONObject) && !(currObj instanceof JSONObjectWrapper)) currObj = new JSONObjectWrapper((JSONObject)currObj); if(currObj instanceof JSONObjectWrapper || !gxColl.IsSimpleCollection()) { diff --git a/java/src/test/java/com/genexus/xml/GXXMLSerializableTest.java b/java/src/test/java/com/genexus/xml/GXXMLSerializableTest.java new file mode 100644 index 000000000..064e835bc --- /dev/null +++ b/java/src/test/java/com/genexus/xml/GXXMLSerializableTest.java @@ -0,0 +1,226 @@ +package com.genexus.xml; + +import com.genexus.ModelContext; +import com.genexus.specific.java.Connect; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class GXXMLSerializableTest { + + private TestSerializable testObj; + + @Before + public void setUp() { + Connect.init(); + testObj = new TestSerializable(new ModelContext(TestSerializable.class), "TestType"); + } + + @Test + public void testFromJSonStringWithSimpleFields() { + // Arrange + String json = "{\"stringField\":\"test value\",\"intField\":42,\"boolField\":true,\"doubleField\":3.14}"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertTrue("JSON parsing should succeed", result); + assertEquals("String field should match", "test value", testObj.getStringField()); + assertEquals("Int field should match", 42, testObj.getIntField()); + assertTrue("Boolean field should match", testObj.isBoolField()); + assertEquals("Double field should match", 3.14, testObj.getDoubleField(), 0.001); + } + + @Test + public void testFromJSonStringWithNullFields() { + // Arrange + String json = "{\"stringField\":null,\"intField\":null,\"boolField\":null,\"doubleField\":null}"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertTrue("JSON parsing should succeed", result); + assertNull("String field should be null", testObj.getStringField()); + assertEquals("Int field should be default", 0, testObj.getIntField()); + assertFalse("Boolean field should be default", testObj.isBoolField()); + assertEquals("Double field should be default", 0.0, testObj.getDoubleField(), 0.001); + } + + @Test + public void testFromJSonStringWithNestedObject() { + // Arrange + String json = "{\"stringField\":\"parent\",\"nestedObject\":{\"stringField\":\"child\"}}"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertTrue("JSON parsing should succeed", result); + assertEquals("Parent string field should match", "parent", testObj.getStringField()); + assertNotNull("Nested object should not be null", testObj.getNestedObject()); + assertEquals("Nested string field should match", "child", testObj.getNestedObject().getStringField()); + } + + @Test + public void testFromJSonStringWithCollection() { + // Arrange + String json = "{\"stringField\":\"parent\",\"items\":[{\"stringField\":\"item1\"},{\"stringField\":\"item2\"}]}"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertTrue("JSON parsing should succeed", result); + assertEquals("Parent string field should match", "parent", testObj.getStringField()); + assertNotNull("Items collection should not be null", testObj.getItems()); + assertEquals("Items collection should have correct size", 2, testObj.getItems().size()); + assertEquals("First item should match", "item1", (testObj.getItems().item(1)).getStringField()); + assertEquals("Second item should match", "item2", (testObj.getItems().item(2)).getStringField()); + } + + @Test + public void testFromJSonStringWithInvalidJson() { + // Arrange + String json = "{invalid json"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertFalse("JSON parsing should fail", result); + } + + @Test + public void testFromJSonStringWithEmptyJson() { + // Arrange + String json = "{}"; + + // Act + boolean result = testObj.fromJSonString(json); + + // Assert + assertTrue("Empty JSON parsing should succeed", result); + assertNull("String field should be null", testObj.getStringField()); + } + + // Test implementation of GXXMLSerializable for testing purposes + public static class TestSerializable extends GXXMLSerializable { + private String stringField; + private int intField; + private boolean boolField; + private double doubleField; + private TestSerializable nestedObject; + private TestSerializableCollection items; + + public TestSerializable() { + super(new ModelContext(TestSerializable.class), "TestType"); + } + + public TestSerializable(ModelContext context) { + super(context, "TestType"); + } + + public TestSerializable(ModelContext context, String type) { + super(context, type); + } + + @Override + public String getJsonMap(String value) { + return value; + } + + @Override + public void initialize() { + items = new TestSerializableCollection(); + } + + // Getter and setter methods with patterns expected by GXXMLSerializable + public String getgxTv_GXXMLSerializableTest$TestSerializable_StringField() { + return stringField; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_StringField(String value) { + this.stringField = value; + } + + public int getgxTv_GXXMLSerializableTest$TestSerializable_IntField() { + return intField; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_IntField(int value) { + this.intField = value; + } + + public boolean getgxTv_GXXMLSerializableTest$TestSerializable_BoolField() { + return boolField; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_BoolField(boolean value) { + this.boolField = value; + } + + public double getgxTv_GXXMLSerializableTest$TestSerializable_DoubleField() { + return doubleField; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_DoubleField(double value) { + this.doubleField = value; + } + + public TestSerializable getgxTv_GXXMLSerializableTest$TestSerializable_NestedObject() { + return nestedObject; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_NestedObject(TestSerializable value) { + this.nestedObject = value; + } + + public TestSerializableCollection getgxTv_GXXMLSerializableTest$TestSerializable_Items() { + return items; + } + + public void setgxTv_GXXMLSerializableTest$TestSerializable_Items(TestSerializableCollection value) { + this.items = value; + } + + // Convenience getters for simpler test code + public String getStringField() { + return stringField; + } + + public int getIntField() { + return intField; + } + + public boolean isBoolField() { + return boolField; + } + + public double getDoubleField() { + return doubleField; + } + + public TestSerializable getNestedObject() { + return nestedObject; + } + + public TestSerializableCollection getItems() { + return items; + } + } + + // Collection class needed for collection tests + public static class TestSerializableCollection extends com.genexus.GXSimpleCollection { + public TestSerializableCollection() { + super(TestSerializable.class, "TestSerializable", "TestCollection"); + } + + @Override + public TestSerializable item(int i) { + return get(i-1); + } + } +} \ No newline at end of file From 4856d0d92d8342a2e5cab4adde9664939efd475e Mon Sep 17 00:00:00 2001 From: iroqueta Date: Thu, 22 May 2025 18:57:23 -0300 Subject: [PATCH 2/2] Fix FromJson and ToJson methods performance caused by json lib changes --- common/src/main/java/com/genexus/GXBaseCollection.java | 2 +- common/src/main/java/com/genexus/json/JSONObjectWrapper.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/com/genexus/GXBaseCollection.java b/common/src/main/java/com/genexus/GXBaseCollection.java index 880faddff..9e123cc42 100644 --- a/common/src/main/java/com/genexus/GXBaseCollection.java +++ b/common/src/main/java/com/genexus/GXBaseCollection.java @@ -233,7 +233,7 @@ public void FromJSONObject(Object obj) try { Object jsonObj = jsonArr.get(i); - if (jsonObj instanceof JSONObject) + if ((jsonObj instanceof JSONObject) && !(jsonObj instanceof JSONObjectWrapper)) jsonObj = new JSONObjectWrapper((JSONObject)jsonObj); Class[] parTypes = new Class[] {}; Object[] arglist = new Object[] {}; diff --git a/common/src/main/java/com/genexus/json/JSONObjectWrapper.java b/common/src/main/java/com/genexus/json/JSONObjectWrapper.java index fe33bcb70..d17b9fa6f 100644 --- a/common/src/main/java/com/genexus/json/JSONObjectWrapper.java +++ b/common/src/main/java/com/genexus/json/JSONObjectWrapper.java @@ -44,7 +44,8 @@ public JSONObjectWrapper(Map m) { } public JSONObjectWrapper(JSONObject jsonObject) { - this(jsonObject.toMap()); + super(jsonObject.toString()); + initMap(); } private void initMap() {