Question

if statement in jsonTemplater


Is there any possibility to use an if statement similar to the xmlTemplater: https://docs.safe.com/fme/html/FME_Desktop_Documentation/FME_Transformers/Transformers/xmltemplater.htm

The following code in the jsonTemplater returns the error

 

"JSONTemplater_2(XMLTemplaterFactory): The following error occurred near line 2, column 50 of the query:

JSONTemplater_2(XMLTemplaterFactory): invalid expression: syntax error, unexpected ExprSingle (missing comma "," between expressions?)"

Suggestions and code samples would be very welcomed.

{
    if (fme:get-attribute("IS_GRENZPUNKT") eq "1")
    then
        "1": {}
    else
       "2": {}
}

12 replies

Userlevel 2
Badge +17

Hi @hire, this expression might work for you.

'{'||{
    if(xs:string(fme:get-attribute("IS_GRENZPUNKT")) eq "1")
    then '"1":{}'
    else '"2":{}'
}||'}'

Hi @hire, this expression might work for you.

'{'||{
    if(xs:string(fme:get-attribute("IS_GRENZPUNKT")) eq "1")
    then '"1":{}'
    else '"2":{}'
}||'}'

 

Works like a charm, thank you!
Badge

Hi @hire, this expression might work for you.

'{'||{
    if(xs:string(fme:get-attribute("IS_GRENZPUNKT")) eq "1")
    then '"1":{}'
    else '"2":{}'
}||'}'
We can't get this inline "if" to work - any help is appreciated - ... it seems it only works once from the top-level rather than say multiple inline "ifs"?

 

 

{
    "Test1": "Test1",    
    {
            if(xs:string(fme:get-attribute("AGLastModified")) eq "")
            then '' 
            else '"AGLastModified": "' || fme:get-attribute("AGLastModified") || '"'    
    }
    "Test2": "Test2"
}
You *can* have an inline value, but it must have a value:

 

{
    "Test1": "Test1",    
    {
            if(xs:string(fme:get-attribute("AGLastModified")) eq "")
            then 'DELETEME' 
            else 'AGLastModified'
    } :  fme:get-attribute("AGLastModified"),
    "Test2": "Test2"
}
Using from the top-level seems to work, but means we have to repeat everything:

 

 

'{'||{
        if(xs:string(fme:get-attribute("AGLastModified")) eq "")
        then '"Test1": "Test1",
"Test2": "Test2"' 
        else '"Test1": "Test1",
"AGLastModified": "' || fme:get-attribute("AGLastModified") || '",
"Test2": "Test2"'    
} ||'}'
---

 

We ended-up just using an Attribute Manager to conditionally set a new Attribute based on a Private Parameter raw JSON string (rather than using the JSON Templater. Not sure what the benefit of the Templater is TBH. 

 

Userlevel 2
Badge +17

Hi @hire, this expression might work for you.

'{'||{
    if(xs:string(fme:get-attribute("IS_GRENZPUNKT")) eq "1")
    then '"1":{}'
    else '"2":{}'
}||'}'
Hi @peterx, the syntax is a single expression that concatenates '{', expression, and '}' to create an entire JSON document as a single text string, and it may not work to create an element of a document partially.

 

In your case, I think it would be better to use an orthodox XQuery (JSONiq) expression like this.

 

{|
    let $names := ("Test1", "AGLastModified", "Test2")
    let $values := ("Test1", fme:get-attribute("AGLastModified"), "Test2")
    for $name at $i in $names
    where xs:string($values[$i]) ne ""
    return {$name : $values[$i]}
|}
Badge

Hi @hire, this expression might work for you.

'{'||{
    if(xs:string(fme:get-attribute("IS_GRENZPUNKT")) eq "1")
    then '"1":{}'
    else '"2":{}'
}||'}'
Thank Takashi, how would I created a nested structure/array (using same empty rules) below this structure? The syntax is a bit confusing. I have the "items" below as standard FME Attributes... e.g.

 

{ "ID" : "1000", "Name" : "Test-1000", "Category" : "Cat", "Items" : [ { "Item1" : "Value1", "Item2" : "Value2" } ] }
Userlevel 2
Badge +17
Thank Takashi, how would I created a nested structure/array (using same empty rules) below this structure? The syntax is a bit confusing. I have the "items" below as standard FME Attributes... e.g.

 

{ "ID" : "1000", "Name" : "Test-1000", "Category" : "Cat", "Items" : [ { "Item1" : "Value1", "Item2" : "Value2" } ] }
That depends on the structure of input feature attributes.

 

For example, if the feature has these attributes:

 

attribute nameattribute valueID1000NameTest-1000CategoryCatAttributes{0}.Attribute1Value1Attributes{1}.Attribute1Value2

 

 

this template expression:

 

{
    "ID" : fme:get-attribute("ID"),
    "Name" : fme:get-attribute("Name"),
    "Category": fme:get-attribute("Category"),
    "Attributes" : [
        for $value in fme:get-list-attribute("Attributes{}.Attribute1")
        return {"Attribute1" : $value}
    ]
}

returns this JSON document.

 

{
   "ID" : 1000,
   "Name" : "Test-1000",
   "Category" : "Cat",
   "Attributes" : [
      {
         "Attribute1" : "Value1"
      },
      {
         "Attribute1" : "Value2"
      }
   ]
}

 

Userlevel 2
Badge +17
Thank Takashi, how would I created a nested structure/array (using same empty rules) below this structure? The syntax is a bit confusing. I have the "items" below as standard FME Attributes... e.g.

 

{ "ID" : "1000", "Name" : "Test-1000", "Category" : "Cat", "Items" : [ { "Item1" : "Value1", "Item2" : "Value2" } ] }
Hey @peterx, have you changed the JSON document in your comment? My answer above has become completely useless.

 

When you wanted to change the contents of the question, please notify to relevant users explicitly, or post it as a new comment or answer.
Userlevel 2
Badge +17
Thank Takashi, how would I created a nested structure/array (using same empty rules) below this structure? The syntax is a bit confusing. I have the "items" below as standard FME Attributes... e.g.

 

{ "ID" : "1000", "Name" : "Test-1000", "Category" : "Cat", "Items" : [ { "Item1" : "Value1", "Item2" : "Value2" } ] }
If the feature could have these attributes:

 

ID, Name, Category, Item1, Item2
this template expression might help you.
{
    "ID" : fme:get-attribute("ID"),
    "Name" : fme:get-attribute("Name"),
    "Category": fme:get-attribute("Category"),
    "Items" : [
        {|
            for $name in ("Item1", "Item2")
            let $value := fme:get-attribute($name)
            where $value ne ""
            return {$name : $value}
        |}
    ]
Userlevel 2
Badge +17
Thank Takashi, how would I created a nested structure/array (using same empty rules) below this structure? The syntax is a bit confusing. I have the "items" below as standard FME Attributes... e.g.

 

{ "ID" : "1000", "Name" : "Test-1000", "Category" : "Cat", "Items" : [ { "Item1" : "Value1", "Item2" : "Value2" } ] }
If "ID", "Name", and "Category" should also be conformed to the "empty rules", the expression should be:

 

{|
    for $name in ("ID", "Name", "Category")
    let $value := fme:get-attribute($name)
    where xs:string($value) ne ""
    return {$name : $value},
    {
        "Items" : [
            {|
                for $name in ("Item1", "Item2")
                let $value := fme:get-attribute($name)
                where $value ne ""
                return {$name : $value}
            |}
        ]
    }
|}
Note: there is a cast syntax (xs:string($value)) in the first where clause since "ID" could store a numeric value, according to your example.

 

Badge
If "ID", "Name", and "Category" should also be conformed to the "empty rules", the expression should be:

 

{|
    for $name in ("ID", "Name", "Category")
    let $value := fme:get-attribute($name)
    where xs:string($value) ne ""
    return {$name : $value},
    {
        "Items" : [
            {|
                for $name in ("Item1", "Item2")
                let $value := fme:get-attribute($name)
                where $value ne ""
                return {$name : $value}
            |}
        ]
    }
|}
Note: there is a cast syntax (xs:string($value)) in the first where clause since "ID" could store a numeric value, according to your example.

 

Thanks Takashi, I couldn't get it working, so I've moved to using Python.
Userlevel 2
Badge +17
If "ID", "Name", and "Category" should also be conformed to the "empty rules", the expression should be:

 

{|
    for $name in ("ID", "Name", "Category")
    let $value := fme:get-attribute($name)
    where xs:string($value) ne ""
    return {$name : $value},
    {
        "Items" : [
            {|
                for $name in ("Item1", "Item2")
                let $value := fme:get-attribute($name)
                where $value ne ""
                return {$name : $value}
            |}
        ]
    }
|}
Note: there is a cast syntax (xs:string($value)) in the first where clause since "ID" could store a numeric value, according to your example.

 

If you can post a sample data and your JSON template expression, I could check where the issue is.

 

 

Badge +3

I see this question already has some age, but I've also wandered about the possibilities of conditionally inserting JSON objects. My use case is/was that of a REST API, which could start calculations, and in the main JSON object you could specify several calculation options. Most of these calculation options were optional to provide (if not provided, default values would be used for the corresponding options). In the workspace I wanted to provide a method to also conditionally insert these optional JSON objects, based on whether the end user chose to specify a (nonDefault) value for these options at corresponding attributes (as fetched from User Parameters). Initially I solved this by initially using a JSONTemplator with only JSON objects for the required calculation options, and then using a JSONUpdator to insert the conditional calculation objects. 

 

Now looking a bit better at the above discussion and code samples, I think I found a more direct approach that is similar to conditionally inserting XML elements. So for example, let's say there are 4 calculation options, of which the third element is conditional. Then in the XML case I could do something like this:

<CalculationOptions>
    <option_1>{fme:get-attribute("option_1")}</option_1>
    <option_2>{fme:get-attribute("option_2")}</option_2>
    {
        if( fme:has-attribute("option_3") and not(xs:string(fme:get-attribute("option_3")) eq "") )
        then (
            <option_3>{fme:get-attribute("option_3")}</option_3>
        )
        else ()
    }
    <option_4>{fme:get-attribute("option_4")}</option_4>
</CalculationOptions>

which in case the attribute 'option_3' is present and (its string value) is not empty, would lead to the following (assume '@Value(option_3)' = 'C'):

<CalculationOptions>
    <option_1>A</option_1>
    <option_2>B</option_2>
    <option_3>C</option_3>
    <option_4>D</option_4>
</CalculationOptions>

But if the attribute 'option_3' would not be present or (its string value) would be empty, this would lead to:

<CalculationOptions>
    <option_1>A</option_1>
    <option_2>B</option_2>
    <option_4>D</option_4>
</CalculationOptions>

Now by carefully looking at the above discussion, I found that you can use the following code in a JSONTemplator for the analogous JSON case;

 

{|
    {
        "option_1": fme:get-attribute("option_1"),
        "option_2": fme:get-attribute("option_2")
    },
    {
        if( fme:has-attribute("option_3") and not(xs:string(fme:get-attribute("option_3")) eq "") )
        then (
            {
                "option_3": fme:get-attribute("option_3")
            }
        )
        else ( {} )
    },
    {
        "option_4": fme:get-attribute("option_4")
    }
|}

In case the attribute 'option_3' is present and (its string value) is not empty, this would lead to the following (assume '@Value(option_3)' = 'C'):

{
   "option_1" : "A",
   "option_2" : "B",
   "option_3" : "C",
   "option_4" : "D"
}

But if the attribute 'option_3' would not be present or (its string value) would be empty, this would lead to:

{
   "option_1" : "A",
   "option_2" : "B",
   "option_4" : "D"
}

Just like takashi's last examples, the '{| <JSON_OBJECTS> |}' is used for dynamic object construction, which merges all the objects returned by the inner expression into a single object with a so-called "simple object union". See also example 4.3 of the JSONiq specification: https://www.jsoniq.org/docs/JSONiqExtensionToXQuery/html-single/index.html#idm48114976

 

The confusing part is maybe that for the code in the JSONTemplator that for the dynamic second JSON object (which contains the third option) you need an initial {} parentheses to have FME execute the JSONiq (kind of xQuery for JSON) expression, whereas in the return statements you again use the {} parentheses to create a valid JSON Object (an empty JSON object is used so that in the merging step no objects are merged).

 

That said, I think my above example is a bit of a hybrid mode. In case you want to merge many (potentially) conditional JSON objects, I think the last example of takashi is more elegant to use.

 

Hope this explanation may be usefull to fellow FMEers :)

 

Reply