Json key order and empty value vs no key

Hi All,

I’ve got two slightly related problems that i’m hoping you can help me with.
My first issue is that one of the tools I use to consume the JSON produced by my form behaves differently depending on the order of the keys that are present in my json object e.g.

{
    "name": "James",
    "dob": "01.01.92"
}

can produce a different result to:

{
    "dob": "01.01.92",
    "name": "James"
}

I’ve noticed that in JSON forms the order that the form is filled in determines the order of the key value pairs in the JSON so if I populate dob first in my form then i’ll get the second example whereas if I populate name first in my form then i’ll get the first example. Is there any way to reorder the JSON so that the keys appear in the order they appear in the JSON schema?

My second issue is that the same tool I use to consume the JSON treats empty objects/values differently to them not being present in the JSON at all. For example

{
    "name": "James"
}

get’s treated differently to:

{
    "name": "James",
    "dob": "",
    "friends": []
}

and that when using JSON forms, if I fill in a string value and then delete it or create an array object and then delete it, it leaves the key in the JSON with an empty value/object which is different to if i’d never populated it at all. I think for this one I could just write a cleanup script that removes empty keys from the JSON when I export my JSON from my form but was wondering if there would be any better approach?

Thanks again!

Hi!

I’ve noticed that in JSON forms the order that the form is filled in determines the order of the key value pairs in the JSON so if I populate dob first in my form then i’ll get the second example whereas if I populate name first in my form then i’ll get the first example. Is there any way to reorder the JSON so that the keys appear in the order they appear in the JSON schema?

In theory you could accomplish this by replacing everything with custom renderers (basically writing your own renderer set) and always replacing the whole data structure when updating. However that’s not a pragmatical approach. Instead I would like to recommend processing the data you get from JSON Forms so it looks like you expect (i.e. order the keys in the order you expect).

My second issue is that the same tool I use to consume the JSON treats empty objects/values differently to them not being present in the JSON at all.

At the moment this behavior can only be customized via custom renderers (probably it’s sufficient to do this for strings and arrays). It’s usually a very simple customization, as you can just invoke the existing renderers but hand over a modified handleChange or removeItem callback which additionally removes the property when it’s empty.

I think for this one I could just write a cleanup script that removes empty keys from the JSON when I export my JSON from my form but was wondering if there would be any better approach?

That’s a very reasonable suggestion which I think would be the easiest solution. Especially so when you already process the data to order the keys anyway.

To note: We might change the string behavior default to always remove the key too for 3.0 as this would be a good point for this breaking change. For the array renderer I would rather see this as a configuration option.

1 Like

thanks as always for the feedback @sdirix

with regards to the ordering, another use case for this is that we use git to version control the JSON and so changes in the order of the fields can appear as changes in git which is a bit of a pain

I managed to solve the problem of sorting my JSON by the properties in my JSON schema.

// This function orders the JSON data in the same order the properties are defined in the JSON schema
export function orderJsonUsingSchema(data: any, jsonSchema: any): any {
    function recurse(data: any, jsonSchema: any): any {
        var levelCopy: any = {};
        for (const[key, value] of Object.entries(jsonSchema)){
            switch (jsonSchema[key].type) {
                case 'object':
                    levelCopy[key] = recurse(data[key], jsonSchema[key].properties);
                    break;
                case 'array':
                    if (data[key]){
                        levelCopy[key] = data[key].map((item: any)=>{
                            // array of objects
                            if (jsonSchema[key].items.type==='object'){
                                return recurse(item, jsonSchema[key].items.properties);
                            }
                            // array of strings
                            else if (jsonSchema[key].items.type==='string'){
                                return item;
                            }
                        })
                    }
                    break;
                default:
                    if (data[key]){
                        levelCopy[key]=data[key];
                    }
            }
        }
        return levelCopy
    }
    return recurse(data, jsonSchema.properties);
}

This function uses a resolved json schema as an input

Looks good! Note that there are also libraries like json-schema-traverse which you could also use to avoid writing your own iterating code. However if your code is sufficient then you save a dependency :wink:

1 Like