Question
· Aug 6, 2019

Preserving default value of a method parameter when passing a non existing property of a JSON object

Hi,

I have a method that has multiple parameters and is normally used from other classes in ObjectScript. Most of the parameters have default values. Withing the server code that's perfect. In some areas I can call myMethod(,,"sometext") or myMethod(tVariab) and thats fine... I use it as it's required in each place.

But now I want to expose it as a REST service so I need to buld a kind of wrapper., another method in a REST dispatcher class that will receive the parameters in a JSON object and will call the server method already implemented. Something like:

{"par1":1111, "par2":"sample text", "par3":0}

that is transformed to a tJSON %DynamicObject that later will be used to call the method: myMethod(tJSON.par1,tJSON.par2,tJSON.par3) ... so far so good.

BUT, I hoped that it would be posible to receive:

{"par1:2222} and then run myMethod(tJSON.par1,tJSON.par2,tJSON.par3)... and, as par2 and par3 really don't exist this time, I expected myMethod assume default values for par2 and par3 parameters. But, in the end, IRIS assumes that I'm passing "" in those positions as if call statement was: myMethod(2222,"","") instead of myMethod(2222).

I want to avoid to implement in the wrapper all possible combinations of input parameters... which would be the best approach?

Discussion (3)0
Log in or sign up to continue

Hi Sergio,

I have the following code that will do what you need, but i don't see this as the best approach since it is more complicated than a bunch of If's. 

But it works, and it shows that you do anything in ObjectScript!

Class MyPackage.MyClass
{

ClassMethod MyMethod(p1 = 1, p2 = 2, p3 = 3)
{
Write p1,"-",p2,"-",p3,!
}

ClassMethod Test()
{
Do ..FromJson({})
Do ..FromJson({"P1":"first value""P2":"second value"})
Do ..FromJson({"P1":"first value""P3":"third value"})
Do ..FromJson({"P2":"second value"})
}

ClassMethod FromJson(json)
{
Set call="(json) Do ##class(MyPackage.MyClass).MyMethod("  //we need to pass json as input param to xecute
Set first = 1
For jsonProp = "P1","P2","P3" {
If 'first Set call=call_","
//Set call=call_$Select(json.%IsDefined(jsonProp):"json."_jsonProp,1:"") //same as $Property, but doc mentions to use $Property instead
Set call=call_$Select(json.%IsDefined(jsonProp):"$Property(json,"""_jsonProp_""")",1:"")
Set first = 0
}
Set call=call_")"
Xecute (call,json) //pass json as input parameter
}

}

Here's a way without indirection:

Class MyPackage.MyClass
{

ClassMethod MyMethod(p1 = 1, p2 = 2, p3 = 3)
{
    Write p1,"-",p2,"-",p3,!
}

/// do ##class(MyPackage.MyClass).Test()
ClassMethod Test()
{
    Do ..FromJson({})
    Do ..FromJson({"p1":"first value", "p2":"second value"})
    Do ..FromJson({"p1":"first value", "p3":"third value"})
    Do ..FromJson({"p2":"second value"})
}

/// do ##class(MyPackage.MyClass).ArgPosMapping("MyPackage.MyClass", "MyMethod")
ClassMethod ArgPosMapping(class, method, Output map)
{
    kill map
    set formalspec = $$$comMemberKeyGet(class,$$$cCLASSmethod,method,$$$cMETHformalspecparsed)
    for i=1:1:$ll(formalspec) {
        set arg = $lg(formalspec, i)
        set map($lg(arg, 1)) =  i
    }
    
    set map = $ll(formalspec)
}

ClassMethod FromJson(json)
{
    Do ..ArgPosMapping($classname(), "MyMethod", .map)
    Set iterator = json.%GetIterator()
    Set position = 0
    While iterator.%GetNext(.key, .value) {
        Set position = position + 1
        Set arguments(map(key)) = value
    }
    
    Set arguments = map

    Do ..MyMethod(arguments...)
}

}

You're running into the fact that calling %Get on a dynamic object doesn't return an undefined when data at the index you specify doesn't exist.  Instead, it returns "".

Both the answers you have here are great if you need a generalized solution.  If all you're looking for is a simple work around in a non-general case, all you need to do is check if your %Get returned "",  and if so, kill the value before you pass it in:

 ClassMethod passIncompleteJSON()
{
  set json = ["arg1 val", "arg2 val"]
  set arg1 = json.%Get(0)
  kill:(arg1="") arg1
  set arg2 = json.%Get(1)
  kill:(arg2="") arg2
  set arg3 = json.%Get(2)
  kill:(arg3="") arg3
  write ..threeArgs(.arg1, .arg2, .arg3)
}

ClassMethod threeArgs(a, b, c)
{
  set:($get(c)="") c = "default"
  return a_"."_b_"."_c
}

Just be sure to pass in the parameters by reference since you're going to be passing in undefined variables.

(This does remove the distinction between undefined and "" though; you wouldn't be able to intentionally pass "" with this solution)