Search code examples
javascriptjsonpropertiesundefinedsimplify

JSON undefined property - simplified validation to avoid TypeErrors


I am attempting to efficiently validate multi-layered json objects which may have one or more undefined parent properties depending on the scenario.

This is an example of and object just 2 levels of properties:

scenarioArray = [ 
{color:{blue:'yes'}},
{color:{green:'no'}},
{colour:{blue:'yes'}} ]; //colour instead of color

So I want to most efficiently identify a scenario's color

let color;
let s = randomScenario; //random scenario from the scenarioArray
if(s.color != undefined && s.color.blue != undefined) {
    color = s.color.blue;
} else if (s.color != undefined && s.color.green != undefined) {
    color = s.color.green;
} else if (s.colour != undefined && s.colour.blue !=undefined) {
    color = s.colour.blue;
};

This code technically works, it is just messy.

When you extrapolate this into objects with 4-5 levels of properties, the if statements become unwieldy and unreadable.

I would love suggestions on how to simply this.

real world json object examples:

addendum.contract.buyerInformation.name 
addendum.contract.contract.buyerInformation.name
addendum.contract.purchaser.buyerFirstName
addendum.contract.data.purchaser.buyer.name

frameworks/libraries used: AngularJS Lodash (open to any suggested libraries that could make this more efficient too)

P.S. please let me know if this is too open ended of a question for stackOverflow...


Solution

  • Unfortunately, JavaScript lacks native autovivification, but it can be emulated via Proxy to some degree.

    This is an awkward and brittle solution, but it reduces any-deep-check to two actions and should work for most cases:

    'use strict';
    
    const isEmpty = Symbol('isEmpty');
    
    function autoVivify(object = {}) {
      Object.defineProperty(object, isEmpty, {
        get: () => Object.keys(object).length === 0,
      });
    
      Object.keys(object).forEach((key) => {
        const val = object[key];
        if (typeof val === 'object' && val !== null) object[key] = autoVivify(val);
      });
    
      return new Proxy(
        object,
        { get: (obj, name) => name in obj ? obj[name] : obj[name] = autoVivify() },
      );
    }
    
    const scenarioArray = [
        { color: { blue: 'yes' }},
        { color: { green: 'no' }},
        { colour: { blue: 'yes' }},
    ];
    
    const randomScenario = autoVivify(scenarioArray[0]);
    
    if (randomScenario.color.blue && !randomScenario.color.blue[isEmpty])
      console.log(randomScenario.color.blue);
    
    if (randomScenario.color.red && !randomScenario.color.red[isEmpty])
      console.log(randomScenario.color.red);
    
    if (randomScenario.color.a.b.c && !randomScenario.color.a.b.c[isEmpty])
      console.log(randomScenario.color.a.b.c);
    
    if (randomScenario.colour.green && !randomScenario.colour.green[isEmpty])
      console.log(randomScenario.colour.blue);
    

    Let's hope we will have a more appropriate way soon.