Would like be able to run a!toJson on a CDT where blank fields are suppressed

Hi,

I'm using some CDTs to generate JSON to an Integration, and would like to *not* have JSON attributes where a Text field is blank.  By default, all the attributes for my CDT are generated in the JSON string when I run the CDT through a!toJson.

Is there any way I can call a!toJson to optionally suppress blank attributes?  Or, has someone written a hack that applies this kind of "filter" after the jsonString has been generated?

I see C# and Java solutions for this kind of thing, but not sure how to do this in Appian.  Is there a plugin, something on AppMarket, etc.?

Thanks for any help!

  Discussion posts and replies are publicly visible

Parents
  • What's the actual problem here? Assuming that sometimes an attribute as a value and sometimes it doesn't then the recipient of the integration call will expect to receive messages with this attribute present, and if the value is empty that is still valid, isn't it? Are you able to elaborate on why you're trying to exclude blank attributes from your JSON message?

  • It's really more of a limitation of the downstream integration.

    I could be doing an update on a single field of an existing entity of a downstream system with an integration.  Ideally, I would just pass the single field as part of that entity's CDT JSON structure, rather than all the fields from the CDT for that given entity.

    Providing a blank value for an existing field could potentially be seen by the downstream integration as a removal of the field rather than just the fact that I'm just not setting it.  This is the main issue.  I don't control the integration, so I am trying to handle things with Appian.  If the field isn't passed at all by Appian, it is ignored by the downstream Integration.  

    As an alternative, one could arguably pass *everything* on the CDT instead of the single field, but that's basically providing more data than necessary, and isn't really a correct reflection of the update to the entity -- it's a single field I'm updating, any other fields I'm passing are extra noise and shouldn't really be updated *again*.

    I don't think suppressing blank fields in JSON is that unusual an ask, is it?

  • We had to create a workaround for some behavior where toJson added a "Z" to pur date values. This is not exactly out of standard, but some JSON libraries commonly used do not support this. We made up a recursive expression which turns the CDT into a map while keeping our own date format.

    Following this pattern you can decide for yourself whether to add a field to the outgoing map. This map can then be converted to JSON in the integration.

  • I did spend some time looking at this.

    Using CDTs is a no-go. The structure is already concrete when you design the CDT, so you'll get those attributes generated in a CDT instance and carried forwards when you cast to JSON.

    Using a Dictionary has more possibilities, but there are still constraints with this method. You would have to write an if() statement around every attribute that has the possibility of not being included in the final payload, but you're backed into a corner that looks like this:

    a!localVariables(
      local!payload: { 
        { firstName: ri!firstName },
        {lastName: ri!lastName},
        if(
          fn!isnull(ri!dob),
          {},
          {dob: ri!dob}
        )
      },
      a!tojson(
        local!payload
      )
    )

    ...where output looks like this:

    [{"firstName":"Stewart"},{"lastName":"Burchell"}]

    (when 'dob' is passed as a null value)

    I think you need to go back to first principles. What is the 'contract' of the service that you're calling? (this might be expressed in a document e.g. MS Word, or as a technical Schema). The contract should say what the specific attributes that are required or optional, what type they should be, whether they're single values or an array of values, even what are the valid values. It should also define what the expected behaviour is e.g. "If you pass an empty value in Attribute A then this will cause that value to be set to null"

  • As a follow-on from this you could construct a generic implementation that generates messages with name/value pairs, only if the attribute contains a non-null/blank value. To future proof this, you can dynamically extract all of the attribute names from a CDT then, with that list of names index() into the CDT to get the corresponding value, generate a {name: xxx, value: yyy} where thee value is no-null/blank, and then transform to JSON:

    a!localVariables(
      /* local!attributes contiand the name of each attribute */
      /* construict by creating an instance of the target CDT, */
      /* make a string version of it, remove all of extraneous characters */
      /* and then split on the comma character*/
      local!attributes: split(
        reduce(
          fn!substitute,
          fn!tostring(
            'type!{urn:com:appian:types:SJB}SJB_customer'()
          ),
          { "[", "]", "=", " " },
          null
        ),
        ","
      ),
      a!tojson(
        a!forEach(
          items: local!attributes,
          expression: if(
            or(
              isnull(fn!index(ri!customer, fv!item, null)),
              
            ),
            {},
            {
              name: fv!item,
              value: fn!index(ri!customer, fv!item, null)
            }
          )
        )
      )
    )

    This way if your CDT every changed then this would break. But (referring to my previous response) your target  system would need to be able to receive the payload in this specific format - a JSON array of name/value pairs.

Reply
  • As a follow-on from this you could construct a generic implementation that generates messages with name/value pairs, only if the attribute contains a non-null/blank value. To future proof this, you can dynamically extract all of the attribute names from a CDT then, with that list of names index() into the CDT to get the corresponding value, generate a {name: xxx, value: yyy} where thee value is no-null/blank, and then transform to JSON:

    a!localVariables(
      /* local!attributes contiand the name of each attribute */
      /* construict by creating an instance of the target CDT, */
      /* make a string version of it, remove all of extraneous characters */
      /* and then split on the comma character*/
      local!attributes: split(
        reduce(
          fn!substitute,
          fn!tostring(
            'type!{urn:com:appian:types:SJB}SJB_customer'()
          ),
          { "[", "]", "=", " " },
          null
        ),
        ","
      ),
      a!tojson(
        a!forEach(
          items: local!attributes,
          expression: if(
            or(
              isnull(fn!index(ri!customer, fv!item, null)),
              
            ),
            {},
            {
              name: fv!item,
              value: fn!index(ri!customer, fv!item, null)
            }
          )
        )
      )
    )

    This way if your CDT every changed then this would break. But (referring to my previous response) your target  system would need to be able to receive the payload in this specific format - a JSON array of name/value pairs.

Children
No Data