Skip to content

Commit afe7826

Browse files
committed
Enhance JSON path handling in GetValueAt method and add unit tests for complex message transformations
1 parent 80cc146 commit afe7826

File tree

5 files changed

+173
-30
lines changed

5 files changed

+173
-30
lines changed

src/iop/cls/IOP/Message.cls

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,31 @@ Method GetValueAt(
4747
// Convert pPropertyPath to a a jsonpath
4848
Set tPath = ..ConvertPath(pPropertyPath)
4949

50-
Set tJSON = {}.%FromJSON(..json)
50+
Set pyjson = ##class(%SYS.Python).Import("json")
51+
Set jp = ##class(%SYS.Python).Import("jsonpath_ng")
52+
Set builtins = ##class(%SYS.Python).Builtins()
5153

52-
// Get value at path
53-
Set tValue = tJSON.%Get(tPath)
54+
Set tJSON = pyjson.loads(..json)
55+
Set parser = jp.parse(tPath)
56+
Set matches = parser.find(tJSON)
57+
58+
Set tResult = ""
59+
60+
// Return the first match
61+
if matches."__len__"() = 1 {
62+
Set match = matches."__getitem__"(0)
63+
Set tResult = match."value"
64+
}
65+
ElseIf matches."__len__"() > 1 {
66+
Set tResult = builtins.list()
67+
For i=0:1:matches."__len__"()-1 {
68+
Set match = matches."__getitem__"(i)
69+
Do tResult.append(match."value")
70+
}
71+
}
72+
73+
Return tResult
5474

55-
Return tValue
56-
5775
} Catch ex {
5876
Set pStatus = ex.AsStatus()
5977
Return ""
@@ -62,21 +80,44 @@ Method GetValueAt(
6280

6381
ClassMethod ConvertPath(pPropertyPath As %String) As %String
6482
{
65-
Set tPath = ""
66-
Set tParts = $ListFromString(pPropertyPath, ".")
67-
Set tPartsCount = $ListLength(tParts)
68-
For i = 1:1:tPartsCount {
69-
Set tPart = $ListGet(tParts, i)
70-
If tPart["(" {
71-
Set tIndex = $Extract(tPart, $Find(tPart, "(") + 1, $Find(tPart, ")") - 1)
72-
Set tPart = $Extract(tPart, 1, $Find(tPart, "(") - 1)
73-
Set tPath = tPath _ "." _ tPart _ "[" _ tIndex _ "]"
74-
}
75-
Else {
76-
Set tPath = tPath _ "." _ tPart
83+
// Convert pPropertyPath to a jsonpath just by replacing
84+
// - '()' with '[*]'
85+
// - '(index)' with '[index]'
86+
// - '(' with '[' and ')' with ']'
87+
// - if index is an integer, replace it with [index-1]
88+
Set tPath = pPropertyPath
89+
Set tPath = $Replace(tPath, "()", "[*]")
90+
Set tPath = $Replace(tPath, "(", "[")
91+
Set tPath = $Replace(tPath, ")", "]")
92+
// Process each [] occurrence in the path
93+
Set tPath = ..ProcessBrackets(tPath)
94+
95+
Return tPath
96+
}
97+
98+
ClassMethod ProcessBrackets(pPath As %String) As %String
99+
{
100+
Set tPath = pPath
101+
Set start = $Find(tPath, "[")
102+
While start {
103+
Set end = $Find(tPath, "]", start)
104+
If 'end Quit // No matching closing bracket
105+
106+
// Extract the index between [ and ]
107+
Set tIndex = $Extract(tPath, start, end-2)
108+
109+
// If index is numeric, decrease by 1 (0-based indexing)
110+
If +tIndex {
111+
Set newPath = $Extract(tPath, 1, start-1)
112+
Set newPath = newPath _ (tIndex-1)
113+
Set newPath = newPath _ $Extract(tPath, end-1, *)
114+
Set tPath = newPath
77115
}
116+
117+
// Move past this [] pair for next iteration
118+
Set start = $Find(tPath, "[", end)
78119
}
79-
Return $Extract(tPath, 2, $Length(tPath))
120+
Return tPath
80121
}
81122

82123
Method SetValueAt(

src/tests/cls/ComplexGet.cls

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Class UnitTest.ComplexGet Extends Ens.DataTransformDTL [ DependsOn = (IOP.Message, Ens.StringRequest) ]
2+
{
3+
4+
Parameter GENERATEEMPTYSEGMENTS = 0;
5+
6+
Parameter IGNOREMISSINGSOURCE = 1;
7+
8+
Parameter REPORTERRORS = 1;
9+
10+
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
11+
12+
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
13+
{
14+
<transform sourceClass='IOP.Message' targetClass='Ens.StringRequest' sourceDocType='registerFilesIop.message.ComplexMessage' create='new' language='objectscript' >
15+
<assign value='source.{post.Title}' property='target.StringValue' action='set' />
16+
</transform>
17+
}
18+
19+
}

src/tests/cls/ComplexGetList.cls

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Class UnitTest.ComplexGetList Extends Ens.DataTransformDTL [ DependsOn = (IOP.Message, Ens.StringRequest) ]
2+
{
3+
4+
Parameter GENERATEEMPTYSEGMENTS = 0;
5+
6+
Parameter IGNOREMISSINGSOURCE = 1;
7+
8+
Parameter REPORTERRORS = 1;
9+
10+
Parameter TREATEMPTYREPEATINGFIELDASNULL = 0;
11+
12+
XData DTL [ XMLNamespace = "http://www.intersystems.com/dtl" ]
13+
{
14+
<transform sourceClass='IOP.Message' targetClass='Ens.StringRequest' sourceDocType='registerFilesIop.message.ComplexMessage' create='new' language='objectscript' >
15+
<assign value='source.{list_post(2).Title}' property='target.StringValue' action='set' />
16+
</transform>
17+
}
18+
19+
}

src/tests/registerFilesIop/message.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,23 @@ class FullMessage(Message):
2525
datetime:datetime
2626
time:time
2727

28-
29-
3028
@dataclass
3129
class PostMessage(Message):
32-
3330
Post:PostClass = None
3431
ToEmailAddress:str = None
3532
Found:str = None
3633

34+
@dataclass
35+
class ComplexMessage(Message):
36+
post:PostClass = None
37+
string:str = None
38+
list_str:List[str] = None
39+
list_int:List[int] = None
40+
list_post:List[PostClass] = None
41+
dict_str:Dict[str,str] = None
42+
dict_int:Dict[str,int] = None
43+
dict_post:Dict[str,PostClass] = None
44+
3745
@dataclass
3846
class MyResponse(Message):
3947
value:str = None

src/tests/test_iop_dtl.py

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,70 @@
1+
import os
2+
import pytest
13
import iris
2-
34
from iop._utils import _Utils
5+
from registerFilesIop.message import SimpleMessage, ComplexMessage
6+
7+
@pytest.fixture
8+
def load_cls_files():
9+
path = os.path.join(os.path.dirname(__file__), 'cls')
10+
_Utils.raise_on_error(iris.cls('%SYSTEM.OBJ').LoadDir(path,'cubk',"*.cls",1))
411

5-
def test_register_message_schema():
6-
from registerFilesIop.message import SimpleMessage
7-
_Utils.register_message_schema(SimpleMessage)
12+
@pytest.fixture
13+
def iop_message():
14+
return iris.cls('IOP.Message')._New()
815

9-
iop_schema_name = SimpleMessage.__module__ + '.' + SimpleMessage.__name__
10-
iop_schema = iris.cls('IOP.Message.JSONSchema')._OpenId(iop_schema_name)
16+
@pytest.mark.parametrize("message_class,expected_name", [
17+
(SimpleMessage, f"{SimpleMessage.__module__}.{SimpleMessage.__name__}"),
18+
(ComplexMessage, f"{ComplexMessage.__module__}.{ComplexMessage.__name__}")
19+
])
20+
def test_register_message_schema(message_class, expected_name):
21+
_Utils.register_message_schema(message_class)
22+
iop_schema = iris.cls('IOP.Message.JSONSchema')._OpenId(expected_name)
1123
assert iop_schema is not None
12-
assert iop_schema.Category == iop_schema_name
13-
assert iop_schema.Name == iop_schema_name
14-
24+
assert iop_schema.Category == expected_name
25+
assert iop_schema.Name == expected_name
26+
27+
@pytest.mark.parametrize("json_data,classname,path,expected", [
28+
('{"string":"Foo", "integer":42}', 'registerFilesIop.message.SimpleMessage', 'string', 'Foo'),
29+
('{"post":{"Title":"Foo"}, "string":"bar", "list_str":["Foo","Bar"]}', 'registerFilesIop.message.ComplexMessage', 'post.Title', 'Foo'),
30+
('{"post":{"Title":"Foo"}, "list_post":[{"Title":"Bar"},{"Title":"Foo"}]}', 'registerFilesIop.message.ComplexMessage', 'list_post(2).Title', 'Foo'),
31+
('{"list_str":["Foo","Bar"]}', 'registerFilesIop.message.ComplexMessage', 'list_str(2)', 'Bar'),
32+
('{"list_str":["Foo","Bar"]}', 'registerFilesIop.message.ComplexMessage', 'list_str()', ['Foo','Bar']),
33+
('{"list_str":["Foo","Bar"]}', 'registerFilesIop.message.ComplexMessage', 'list_str', ['Foo','Bar']),
34+
('{"list":["Foo","sub_list":["Bar","Baz"]]}', 'registerFilesIop.message.ComplexMessage', 'list().sub_list(2)', 'Baz'),
35+
])
36+
def test_get_value_at(iop_message, json_data, classname, path, expected):
37+
iop_message.json = json_data
38+
iop_message.classname = classname
39+
result = iop_message.GetValueAt(path)
40+
assert result == expected
41+
42+
@pytest.mark.parametrize("json_data,classname,transform_class,expected_value", [
43+
(
44+
'{"string":"Foo", "integer":42}',
45+
'registerFilesIop.message.SimpleMessage',
46+
'UnitTest.SimpleMessageGet',
47+
'Foo'
48+
),
49+
(
50+
'{"post":{"Title":"Foo"}, "string":"bar", "list_str":["Foo","Bar"]}',
51+
'registerFilesIop.message.ComplexMessage',
52+
'UnitTest.ComplexGet',
53+
'Foo'
54+
),
55+
(
56+
'{"post":{"Title":"Foo"}, "list_post":[{"Title":"Bar"},{"Title":"Foo"}]}',
57+
'registerFilesIop.message.ComplexMessage',
58+
'UnitTest.ComplexGetList',
59+
'Foo'
60+
)
61+
])
62+
def test_transform(load_cls_files, iop_message, json_data, classname, transform_class, expected_value):
63+
ref = iris.ref(None)
64+
iop_message.json = json_data
65+
iop_message.classname = classname
66+
67+
iris.cls(transform_class).Transform(iop_message, ref)
68+
result = ref.value
69+
70+
assert result.StringValue == expected_value

0 commit comments

Comments
 (0)