David Brierton
David Brierton

Reputation: 7397

JSON to CSV flattening nested JSON

I am trying to Flatten JSON to parse as a CSV. But the flattening is not properly flattening. When I get the json to flatten customer.addresses is filling with addresstype:r then skipping all fields city,countrycode,countycode etc. and then starting at customer.companyName. The nested JSON is not breaking up properly to show properly in excel I think my JavaScript code must be off just a little bit. Any help with this would be greatly appreciated.

JSON (this is a portion of the nested json it will not always be in the same depth is there a way to code for any type of nested json that will read at all levels)

[
  {
    "countyCode": 12,
    "customer": {
      "addresses": [
        {
          "addressType": "R",
          "city": "BRADENTON",
          "countryCode": "US",
          "countyCode": 12,
          "foreignPostalCode": null,
          "state": "FL",
          "streetAddress": "819 15th Ave Dr E",
          "zipCode": 34211,
          "zipPlus": null
        },
        {
          "addressType": "M",
          "city": "BRADENTON",
          "countryCode": "US",
          "countyCode": 12,
          "foreignPostalCode": null,
          "state": "FL",
          "streetAddress": "PO BOX 124",
          "zipCode": 34201,
          "zipPlus": 0124
        }
      ],
      "companyName": null,
      "customerNumber": 932874,
      "customerStopFlag": false,
      "customerType": "I",
      "dateOfBirth": "1936-08-05T00:00:00",
      "dlExpirationDate": "2022-08-05T00:00:00",
      "dlRenewalEligibilityFlag": true,
      "driverLicenseNumber": "B360722339284",
      "emailAddress": null,
      "feidNumber": null,
      "firstName": "David",
      "lastName": "Brierton",
      "middleName": "Hugh",
      "militaryExemptionFlag": null,
      "nameSuffix": null,
      "sex": "M"

JS

function flatObjectToString(obj) {
    var s = "";
    Object.keys(obj).map(key => {
      if (obj[key] === null) {
        s += key + ":";
      } else if (obj[key].toLocaleDateString) {
        s += key + ": " + obj[key].toLocaleDateString() + "\n";
      } else if (obj[key] instanceof Array) {
        s += key + ":\n" + listToFlatString(obj[key]);
      } else if (typeof obj[key] == "object") {
        s += key + ":\n" + flatObjectToString(obj[key]);
      } else {
        s += key + ":" + obj[key];
      }
      s += "\n";
    });
    return s;
  }

  function listToFlatString(list) {
    var s = "";
    list.map(item => {
      Object.keys(item).map(key => {
        s += "";
        if (item[key] instanceof Array) {
          s += key + "\n" + listToFlatString(item[key]);
        } else if (typeof item[key] == "object" && item[key] !== null) {
          s += key + ": " + flatObjectToString(item[key]);
        } else {
          s += key + ": " + (item[key] === null ? "" : item[key].toLocaleDateString ? item[key].toLocaleDateString : item[key].toString());
        }
        s += "\n";
      });
    });
    return s;
  }

  function flatten(object, addToList, prefix) {
    Object.keys(object).map(key => {
      if (object[key] === null) {
        addToList[prefix + key] = "";
      } else
      if (object[key] instanceof Array) {
        addToList[prefix + key] = listToFlatString(object[key]);
      } else if (typeof object[key] == 'object' && !object[key].toLocaleDateString) {
        flatten(object[key], addToList, prefix + key + '.');
      } else {
        addToList[prefix + key] = object[key];
      }
    });
    return addToList;
  }

Then I run it through the Javascript Utilities with this:

// Run the JSON string through the flattening utilities above
          var flatJSON = JSON.parse(evt.target.result).map(record => flatten(record, {}, ''));

          var csv = Papa.unparse(flatJSON);

Upvotes: 17

Views: 12876

Answers (6)

Mehdi Benkirane
Mehdi Benkirane

Reputation: 445

function obj2csv(obj, opt) {
        if (typeof obj !== 'object') return null;
        opt = opt || {};
        var scopechar = opt.scopechar || '.';
        var delimeter = opt.delimeter || ',';
        if (Array.isArray(obj) === false) obj = [obj];
        var curs, name, rownum, key, queue, values = [], rows = [], headers = {}, headersArr = [];
        for (rownum = 0; rownum < obj.length; rownum++) {
            queue = [obj[rownum], ''];
            rows[rownum] = {};
            while (queue.length > 0) {
                name = queue.pop();
                curs = queue.pop();
                if (curs !== null && typeof curs === 'object') {
                    for (key in curs) {
                        if (curs.hasOwnProperty(key)) {
                            queue.push(curs[key]);
                            queue.push(name + (name ? scopechar : '') + key);
                        }
                    }
                } else {
                    if (headers[name] === undefined) headers[name] = true;
                    rows[rownum][name] = curs;
                }
            }
            values[rownum] = [];
        }
        // create csv text
        for (key in headers) {
            if (headers.hasOwnProperty(key)) {
                headersArr.push(key);
                for (rownum = 0; rownum < obj.length; rownum++) {
                    values[rownum].push(rows[rownum][key] === undefined
                                        ? ''
                                        : rows[rownum][key]);//JSON.stringify()
                }
            }
        }
        for (rownum = 0; rownum < obj.length; rownum++) {
            values[rownum] = values[rownum].join(delimeter);

        }

          return '"' + headersArr.join('"' + delimeter + '"') + '"\n' + values.join('\n');
    }

if you you have a json like the following

{
  "_id": "template:user",
  "_rev": "11-d319c4ac632171d6f01c40fdef3164a5",
  "p": "user",
  "first_name": "first_name_000",
  "last_name": "last_name_000",
  "favorite_list": {
    "field": "value_000"
  },
  "list_kmorganisation": [
    {
      "siren": "siren_000",
      "partition": "partition_000",
      "id_role": "id_role_000",
      "is_default": "is_default_000",
      "is_managed": "is_managed_000",
      "is_banned": "is_managed_000",
      "ban_reason": "ban_reason_000",
      "ban_date": "ban_date_000",
      "last_connection": "last_connection_000"
    }
  ],
  "login": {
    "mail": "mail_000",
    "passwd": "passwd_000",
    "salt": "salt_000",
    "status": "status_000",
    "access": [
      {
        "log_date": "log_date_000",
        "os": "os_000",
        "version": "version_000",
        "ip_addr": "ip_addr_000",
        "screen": "screen_000"
      }
    ]
  }
}

the end result will be flattened like this "login__access__0__screen", "login__access__0__ip_addr", "login__access__0__version", "login__access__0__os", "login__access__0__log_date", "login__status", "login__salt", "login__passwd", "login__mail", "last_name","first_name", "list_kmorganisation__1__last_connection", "list_kmorganisation__1__ban_date", "list_kmorganisation__1__ban_reason", "list_kmorganisation__1__is_banned", "list_kmorganisation__1__is_managed", "list_kmorganisation__1__is_default", "list_kmorganisation__1__id_role", "list_kmorganisation__1__partition", "list_kmorganisation__1__cmpny_name", "list_kmorganisation__1__siren", "list_kmorganisation__0__last_connection", "list_kmorganisation__0__ban_date", "list_kmorganisation__0__ban_reason", "list_kmorganisation__0__is_banned", "list_kmorganisation__0__is_managed", "list_kmorganisation__0__is_default", "list_kmorganisation__0__id_role", "list_kmorganisation__0__partition", "list_kmorganisation__0__cmpny_name", "list_kmorganisation__0__siren", "p", "_rev", "_id" (in one line of course and the values in the following line )

Upvotes: 1

Simon
Simon

Reputation: 61

Here is a solution that is readable, and that also parses dates correctly. Several solutions mentioned flattens objects recursively, but does not take into consideration that dates are objects. This is not an issue when using json files as OP mentioned, but when the same flattener-function is used on js-objects the dates will not be parsed correctly.

Prefix inserts one or more prefixes if needed ("my.prefixes.here.0.value")

export const flatten = (obj, prefix = [], current = {}) => {
  const isDate = obj instanceof Date;
  const isObject = typeof obj === "object";
  const notNull = obj !== null;
  const flattenRecursive = !isDate && isObject && notNull;

  if (flattenRecursive) {
    for (const key in obj) {
      flatten(obj[key], prefix.concat(key), current);
    }
  } else {
    current[prefix.join(".")] = obj;
  }
  return current;
};

Edit: Output from function with example-data from op:

{ '0.countyCode': 12,
  '0.customer.addresses.0.addressType': 'R',
  '0.customer.addresses.0.city': 'BRADENTON',
  '0.customer.addresses.0.countryCode': 'US',
  '0.customer.addresses.0.countyCode': 12,
  '0.customer.addresses.0.foreignPostalCode': null,
  '0.customer.addresses.0.state': 'FL',
  '0.customer.addresses.0.streetAddress': '819 15th Ave Dr E',
  '0.customer.addresses.0.zipCode': 34211,
  '0.customer.addresses.0.zipPlus': null,
  '0.customer.addresses.1.addressType': 'M',
  '0.customer.addresses.1.city': 'BRADENTON',
  '0.customer.addresses.1.countryCode': 'US',
  '0.customer.addresses.1.countyCode': 12,
  '0.customer.addresses.1.foreignPostalCode': null,
  '0.customer.addresses.1.state': 'FL',
  '0.customer.addresses.1.streetAddress': 'PO BOX 124',
  '0.customer.addresses.1.zipCode': 34201,
  '0.customer.addresses.1.zipPlus': '0124',
  '0.customer.companyName': null,
  '0.customer.customerNumber': 932874,
  '0.customer.customerStopFlag': false,
  '0.customer.customerType': 'I',
  '0.customer.dateOfBirth': '1936-08-05T00:00:00',
  '0.customer.dlExpirationDate': '2022-08-05T00:00:00',
  '0.customer.dlRenewalEligibilityFlag': true,
  '0.customer.driverLicenseNumber': 'B360722339284',
  '0.customer.emailAddress': null,
  '0.customer.feidNumber': null,
  '0.customer.firstName': 'David',
  '0.customer.lastName': 'Brierton',
  '0.customer.middleName': 'Hugh',
  '0.customer.militaryExemptionFlag': null,
  '0.customer.nameSuffix': null,
  '0.customer.sex': 'M' }

Upvotes: 2

Igor Skoric
Igor Skoric

Reputation: 654

I would recommend using flat and not re-inventing the wheel. If you really don't want to do that you could just read Hugh's code here. He supports both flatten and unflatten. Here is the flatten function for reference:

var isBuffer = require('is-buffer')

module.exports = flatten
flatten.flatten = flatten
flatten.unflatten = unflatten

function flatten (target, opts) {
  opts = opts || {}

  var delimiter = opts.delimiter || '.'
  var maxDepth = opts.maxDepth
  var output = {}

  function step (object, prev, currentDepth) {
    currentDepth = currentDepth || 1
    Object.keys(object).forEach(function (key) {
      var value = object[key]
      var isarray = opts.safe && Array.isArray(value)
      var type = Object.prototype.toString.call(value)
      var isbuffer = isBuffer(value)
      var isobject = (
        type === '[object Object]' ||
        type === '[object Array]'
      )

      var newKey = prev
        ? prev + delimiter + key
        : key

      if (!isarray && !isbuffer && isobject && Object.keys(value).length &&
        (!opts.maxDepth || currentDepth < maxDepth)) {
        return step(value, newKey, currentDepth + 1)
      }

      output[newKey] = value
    })
  }

  step(target)

  return output
}

Upvotes: 2

Tarun Lalwani
Tarun Lalwani

Reputation: 146510

If you are looking for the fix to your actual code then you need to use something like below

function flatten(object, addToList, prefix) {
    Object.keys(object).map(key => {
        if (object[key] === null) {
            addToList[prefix + key] = "";
        } else
        if (object[key] instanceof Array) {
            // addToList[prefix + key] = listToFlatString(object[key]);
            for (i in object[key]) {
                flatten(object[key][i], addToList, prefix + key + "." + i)
            }
        } else if (typeof object[key] == 'object' && !object[key].toLocaleDateString) {
            flatten(object[key], addToList, prefix + key + '.');
        } else {
            addToList[prefix + key] = object[key];
        }
    });
    return addToList;
}

I replace

addToList[prefix + key] = listToFlatString(object[key]);

with

for (i in object[key]) {
   flatten(object[key][i], addToList, prefix + key + "." + i)
}

That fixed the issue and the output is below as per the sample json you provided

[ '0.countyCode': 12,
  '0.excludedFlag': '',
  '0.fees.annualTotalFees': 35.6,
  '0.fees.annualTotalFeesMail': 36.35,
  '0.fees.annualTotalFeesOnline': 38.35,
  '0.fees.biennialTotalFees': 71.2,
  '0.fees.biennialTotalFeesMail': 71.95,
  '0.fees.biennialTotalFeesOnline': 73.95,
  '0.fees.branchFeeFlag': false,
  '0.fees.delinquentFeeAmount': 5,
  '0.fees.mhBackTax': 0,
  '0.fees.mhBackTaxMonths': 0,
  '0.fileId': 522,
  '0.id': '0002be04-546-4a92-a3d7-c31546544f',
  '0.messages.0messageCode': 'RN2',
  '0.messages.0messageText': 'Plate must be replaced if a 2 year renewal is desired.',
  '0.messages.0messageType': 'I',
  '0.messages.1messageCode': 'RN40',
  '0.messages.1messageText': 'You must complete the affidavit on the reverse side or provide a copy of your Florida insurance identification card.',
  '0.messages.1messageType': 'I',
  '0.messages.2messageCode': 'RN41',
  '0.messages.2messageText': 'Insurance is on file.',
  '0.messages.2messageType': 'II',
  '0.registrationDetail.annualPlateReplacementFlag': false,
  '0.registrationDetail.arfCredit': 25.6,
  '0.registrationDetail.biennialPlateReplacementFlag': true,
  '0.registrationDetail.customerStopFlag': '',
  '0.registrationDetail.delinquentDate': '2018-02-11T00:00:00',
  '0.registrationDetail.expirationDate': '2018-01-09T00:00:00',
  '0.registrationDetail.foreignAddressFlag': false,
  '0.registrationDetail.honorayConsulPlateFlag': '',
  '0.registrationDetail.hovDecalNumber': '',
  '0.registrationDetail.hovDecalYear': '',
  '0.registrationDetail.hovExpirationDate': '',
  '0.registrationDetail.inventorySubtype': 'RP',
  '0.registrationDetail.lastActivityCounty': 12,
  '0.registrationDetail.legislativePlateDueForReplacementFlag': false,
  '0.registrationDetail.licensePlateCode': 'RGR',
  '0.registrationDetail.licensePlateNumber': 'L45656',
  '0.registrationDetail.mailToAddressFlag': false,
  '0.registrationDetail.mailToCustomerNumber': '',
  '0.registrationDetail.mhLocationCode': '',
  '0.registrationDetail.militaryOwnerFlag': false,
  '0.registrationDetail.numberOfRegistrants': 1,
  '0.registrationDetail.onlineRenewalEligibilityFlag': true,
  '0.registrationDetail.pinNumber': 222354654,
  '0.registrationDetail.plateExpirationDate': '2018-03-18T00:00:00',
  '0.registrationDetail.plateIssueDate': '2008-09-18T00:00:00',
  '0.registrationDetail.possibleNgExemptionFlag': false,
  '0.registrationDetail.registrationNumber': 2234545345,
  '0.registrationDetail.registrationOnlyFlag': '',
  '0.registrationDetail.registrationType': 'R',
  '0.registrationDetail.registrationUse': 'PR',
  '0.registrationDetail.renewalCountyCode': 12,
  '0.registrationDetail.rentalParkFlag': '',
  '0.registrationDetail.seminoleMiccosukeeIndianFlag': false,
  '0.registrationDetail.taxCollectorRenewalEligibilityFlag': true,
  '0.registrationDetail.vehicleClassCode': 1,
  '0.registrationOwners.0customer.addresses.0addressType': 'R',
  '0.registrationOwners.0customer.addresses.0city': 'PALMETTO',
  '0.registrationOwners.0customer.addresses.0countryCode': 'US',
  '0.registrationOwners.0customer.addresses.0countyCode': 12,
  '0.registrationOwners.0customer.addresses.0foreignPostalCode': '',
  '0.registrationOwners.0customer.addresses.0state': 'FL',
  '0.registrationOwners.0customer.addresses.0streetAddress': '34545 7TH AVE W',
  '0.registrationOwners.0customer.addresses.0zipCode': 34221,
  '0.registrationOwners.0customer.addresses.0zipPlus': '',
  '0.registrationOwners.0customer.addresses.1addressType': 'M',
  '0.registrationOwners.0customer.addresses.1city': 'PALMETTO',
  '0.registrationOwners.0customer.addresses.1countryCode': 'US',
  '0.registrationOwners.0customer.addresses.1countyCode': 12,
  '0.registrationOwners.0customer.addresses.1foreignPostalCode': '',
  '0.registrationOwners.0customer.addresses.1state': 'FL',
  '0.registrationOwners.0customer.addresses.1streetAddress': '34545 7TH AVE W',
  '0.registrationOwners.0customer.addresses.1zipCode': 34221,
  '0.registrationOwners.0customer.addresses.1zipPlus': 3433,
  '0.registrationOwners.0customer.companyName': '',
  '0.registrationOwners.0customer.customerNumber': 3346645,
  '0.registrationOwners.0customer.customerStopFlag': false,
  '0.registrationOwners.0customer.customerType': 'I',
  '0.registrationOwners.0customer.dateOfBirth': '1971-01-09T00:00:00',
  '0.registrationOwners.0customer.dlExpirationDate': '2025-01-09T00:00:00',
  '0.registrationOwners.0customer.dlRenewalEligibilityFlag': true,
  '0.registrationOwners.0customer.driverLicenseNumber': 'F34545345345',
  '0.registrationOwners.0customer.emailAddress': '',
  '0.registrationOwners.0customer.feidNumber': '',
  '0.registrationOwners.0customer.firstName': 'DAVID',
  '0.registrationOwners.0customer.lastName': 'HUGH',
  '0.registrationOwners.0customer.middleName': 'BRIERTON',
  '0.registrationOwners.0customer.militaryExemptionFlag': false,
  '0.registrationOwners.0customer.nameSuffix': '',
  '0.registrationOwners.0customer.sex': 'F',
  '0.registrationOwners.0registrationOwnershipNumber': 1,
  '0.shippingAddress.address.addressType': 'S',
  '0.shippingAddress.address.city': 'PALMETTO',
  '0.shippingAddress.address.countryCode': 'US',
  '0.shippingAddress.address.countyCode': 12,
  '0.shippingAddress.address.foreignPostalCode': '',
  '0.shippingAddress.address.state': 'FL',
  '0.shippingAddress.address.streetAddress': '34545 7TH AVE W',
  '0.shippingAddress.address.zipCode': 34221,
  '0.shippingAddress.address.zipPlus': 8344,
  '0.shippingAddress.shippingCompanyName': '',
  '0.shippingAddress.shippingName1': 'DAVID HUGH BRIERTON',
  '0.shippingAddress.shippingName2': '',
  '0.stops': '',
  '0.vehicle.address': '',
  '0.vehicle.bodyCode': '4D',
  '0.vehicle.brakeHorsePower': '',
  '0.vehicle.cubicCentimeters': '',
  '0.vehicle.grossWeight': '',
  '0.vehicle.insuranceAffidavitCode': '',
  '0.vehicle.leaseOwnerFlag': false,
  '0.vehicle.lengthFeet': '',
  '0.vehicle.lengthInches': '',
  '0.vehicle.majorColorCode': 'GLD',
  '0.vehicle.makeCode': 'CHEV',
  '0.vehicle.minorColorCode': '',
  '0.vehicle.netWeight': 3200,
  '0.vehicle.numberOfAxles': '',
  '0.vehicle.ownerUnitNumber': '',
  '0.vehicle.titleNumber': 345454534,
  '0.vehicle.vehicleIdentificationNumber': '1G1ZD345U5B345435',
  '0.vehicle.vehicleNumber': 23454656,
  '0.vehicle.vehicleType': 'AU',
  '0.vehicle.vesselCode': '',
  '0.vehicle.vesselManufacturerDesc': '',
  '0.vehicle.vesselResidentStatus': 'Y',
  '0.vehicle.vesselWaterType': '',
  '0.vehicle.widthFeet': '',
  '0.vehicle.widthInches': '',
  '0.vehicle.yearMake': 2011,
  '1.countyCode': 12,
  '1.excludedFlag': '',
  '1.fees.annualTotalFees': 27.6,
  '1.fees.annualTotalFeesMail': 28.35,
  '1.fees.annualTotalFeesOnline': 30.35,
  '1.fees.biennialTotalFees': 55.2,
  '1.fees.biennialTotalFeesMail': 55.95,
  '1.fees.biennialTotalFeesOnline': 57.95,
  '1.fees.branchFeeFlag': false,
  '1.fees.delinquentFeeAmount': 5,
  '1.fees.mhBackTax': 0,
  '1.fees.mhBackTaxMonths': 0,
  '1.fileId': 522,
  '1.id': '0008c654-8960-45b8-b416-cff3456767',
  '1.messages.0messageCode': 'RN40',
  '1.messages.0messageText': 'You must complete the affidavit on the reverse side or provide a copy of your Florida insurance identification card.',
  '1.messages.0messageType': 'I',
  '1.registrationDetail.annualPlateReplacementFlag': false,
  '1.registrationDetail.arfCredit': 2.8,
  '1.registrationDetail.biennialPlateReplacementFlag': false,
  '1.registrationDetail.customerStopFlag': '',
  '1.registrationDetail.delinquentDate': '2018-02-11T00:00:00',
  '1.registrationDetail.expirationDate': '2018-01-01T00:00:00',
  '1.registrationDetail.foreignAddressFlag': false,
  '1.registrationDetail.honorayConsulPlateFlag': '',
  '1.registrationDetail.hovDecalNumber': '',
  '1.registrationDetail.hovDecalYear': '',
  '1.registrationDetail.hovExpirationDate': '',
  '1.registrationDetail.inventorySubtype': 'SS',
  '1.registrationDetail.lastActivityCounty': 16,
  '1.registrationDetail.legislativePlateDueForReplacementFlag': false,
  '1.registrationDetail.licensePlateCode': 'RGS',
  '1.registrationDetail.licensePlateNumber': 'HU34598',
  '1.registrationDetail.mailToAddressFlag': false,
  '1.registrationDetail.mailToCustomerNumber': '',
  '1.registrationDetail.mhLocationCode': '',
  '1.registrationDetail.militaryOwnerFlag': false,
  '1.registrationDetail.numberOfRegistrants': 1,
  '1.registrationDetail.onlineRenewalEligibilityFlag': true,
  '1.registrationDetail.pinNumber': 4936856,
  '1.registrationDetail.plateExpirationDate': '2026-09-24T00:00:00',
  '1.registrationDetail.plateIssueDate': '2017-03-24T00:00:00',
  '1.registrationDetail.possibleNgExemptionFlag': false,
  '1.registrationDetail.registrationNumber': 4095685,
  '1.registrationDetail.registrationOnlyFlag': '',
  '1.registrationDetail.registrationType': 'R',
  '1.registrationDetail.registrationUse': 'PR',
  '1.registrationDetail.renewalCountyCode': 12,
  '1.registrationDetail.rentalParkFlag': '',
  '1.registrationDetail.seminoleMiccosukeeIndianFlag': false,
  '1.registrationDetail.taxCollectorRenewalEligibilityFlag': true,
  '1.registrationDetail.vehicleClassCode': 1,
  '1.registrationOwners.0customer.addresses.0addressType': 'R',
  '1.registrationOwners.0customer.addresses.0city': 'SARASOTA',
  '1.registrationOwners.0customer.addresses.0countryCode': 'US',
  '1.registrationOwners.0customer.addresses.0countyCode': 12,
  '1.registrationOwners.0customer.addresses.0foreignPostalCode': '',
  '1.registrationOwners.0customer.addresses.0state': 'FL',
  '1.registrationOwners.0customer.addresses.0streetAddress': '5858 FRUITVILLE RD',
  '1.registrationOwners.0customer.addresses.0zipCode': 34240,
  '1.registrationOwners.0customer.addresses.0zipPlus': 5858,
  '1.registrationOwners.0customer.addresses.1addressType': 'M',
  '1.registrationOwners.0customer.addresses.1city': 'SARASOTA',
  '1.registrationOwners.0customer.addresses.1countryCode': 'US',
  '1.registrationOwners.0customer.addresses.1countyCode': 16,
  '1.registrationOwners.0customer.addresses.1foreignPostalCode': '',
  '1.registrationOwners.0customer.addresses.1state': 'FL',
  '1.registrationOwners.0customer.addresses.1streetAddress': '5858 FRUITVILLE RD',
  '1.registrationOwners.0customer.addresses.1zipCode': 34240,
  '1.registrationOwners.0customer.addresses.1zipPlus': 5858,
  '1.registrationOwners.0customer.companyName': '',
  '1.registrationOwners.0customer.customerNumber': 2928357,
  '1.registrationOwners.0customer.customerStopFlag': false,
  '1.registrationOwners.0customer.customerType': 'I',
  '1.registrationOwners.0customer.dateOfBirth': '1989-01-01T00:00:00',
  '1.registrationOwners.0customer.dlExpirationDate': '2022-01-01T00:00:00',
  '1.registrationOwners.0customer.dlRenewalEligibilityFlag': true,
  '1.registrationOwners.0customer.driverLicenseNumber': 'B94832734',
  '1.registrationOwners.0customer.emailAddress': '',
  '1.registrationOwners.0customer.feidNumber': '',
  '1.registrationOwners.0customer.firstName': 'DAVID1',
  '1.registrationOwners.0customer.lastName': 'HUGH1',
  '1.registrationOwners.0customer.middleName': 'BRIERTON1',
  '1.registrationOwners.0customer.militaryExemptionFlag': false,
  '1.registrationOwners.0customer.nameSuffix': '',
  '1.registrationOwners.0customer.sex': 'M',
  '1.registrationOwners.0registrationOwnershipNumber': 1,
  '1.shippingAddress.address.addressType': 'S',
  '1.shippingAddress.address.city': 'SARASOTA',
  '1.shippingAddress.address.countryCode': 'US',
  '1.shippingAddress.address.countyCode': 16,
  '1.shippingAddress.address.foreignPostalCode': '',
  '1.shippingAddress.address.state': 'FL',
  '1.shippingAddress.address.streetAddress': '5858 FRUITVILLE RD',
  '1.shippingAddress.address.zipCode': 34240,
  '1.shippingAddress.address.zipPlus': 5858,
  '1.shippingAddress.shippingCompanyName': '',
  '1.shippingAddress.shippingName1': 'DAVID1 HUGH1 BRIERTON1',
  '1.shippingAddress.shippingName2': '',
  '1.stops': '',
  '1.vehicle.address': '',
  '1.vehicle.bodyCode': '4D',
  '1.vehicle.brakeHorsePower': '',
  '1.vehicle.cubicCentimeters': '',
  '1.vehicle.grossWeight': '',
  '1.vehicle.insuranceAffidavitCode': '',
  '1.vehicle.leaseOwnerFlag': false,
  '1.vehicle.lengthFeet': '',
  '1.vehicle.lengthInches': '',
  '1.vehicle.majorColorCode': 'BLU',
  '1.vehicle.makeCode': 'STRN',
  '1.vehicle.minorColorCode': '',
  '1.vehicle.netWeight': 2290,
  '1.vehicle.numberOfAxles': '',
  '1.vehicle.ownerUnitNumber': '',
  '1.vehicle.titleNumber': 239874,
  '1.vehicle.vehicleIdentificationNumber': '1G832492871Z23094',
  '1.vehicle.vehicleNumber': 239084,
  '1.vehicle.vehicleType': 'AU',
  '1.vehicle.vesselCode': '',
  '1.vehicle.vesselManufacturerDesc': '',
  '1.vehicle.vesselResidentStatus': 'Y',
  '1.vehicle.vesselWaterType': '',
  '1.vehicle.widthFeet': '',
  '1.vehicle.widthInches': '',
  '1.vehicle.yearMake': 2001,
  '2.countyCode': 12,
  '2.excludedFlag': '',
  '2.fees.annualTotalFees': 45.6,
  '2.fees.annualTotalFeesMail': 46.35,
  '2.fees.annualTotalFeesOnline': 48.35,
  '2.fees.biennialTotalFees': 91.2,
  '2.fees.biennialTotalFeesMail': 91.95,
  '2.fees.biennialTotalFeesOnline': 93.95,
  '2.fees.branchFeeFlag': false,
  '2.fees.delinquentFeeAmount': 10,
  '2.fees.mhBackTax': 0,
  '2.fees.mhBackTaxMonths': 0,
  '2.fileId': 522,
  '2.id': '000e3450d-3454-499a-ae70-de5676577',
  '2.messages.0messageCode': 'RN40',
  '2.messages.0messageText': 'You must complete the affidavit on the reverse side or provide a copy of your Florida insurance identification card.',
  '2.messages.0messageType': 'I',
  '2.registrationDetail.annualPlateReplacementFlag': false,
  '2.registrationDetail.arfCredit': 8.4,
  '2.registrationDetail.biennialPlateReplacementFlag': false,
  '2.registrationDetail.customerStopFlag': '',
  '2.registrationDetail.delinquentDate': '2018-02-11T00:00:00',
  '2.registrationDetail.expirationDate': '2018-01-11T00:00:00',
  '2.registrationDetail.foreignAddressFlag': false,
  '2.registrationDetail.honorayConsulPlateFlag': '',
  '2.registrationDetail.hovDecalNumber': '',
  '2.registrationDetail.hovDecalYear': '',
  '2.registrationDetail.hovExpirationDate': '',
  '2.registrationDetail.inventorySubtype': 'RP',
  '2.registrationDetail.lastActivityCounty': 12,
  '2.registrationDetail.legislativePlateDueForReplacementFlag': false,
  '2.registrationDetail.licensePlateCode': 'RGR',
  '2.registrationDetail.licensePlateNumber': '808IUT',
  '2.registrationDetail.mailToAddressFlag': false,
  '2.registrationDetail.mailToCustomerNumber': '',
  '2.registrationDetail.mhLocationCode': '',
  '2.registrationDetail.militaryOwnerFlag': false,
  '2.registrationDetail.numberOfRegistrants': 1,
  '2.registrationDetail.onlineRenewalEligibilityFlag': true,
  '2.registrationDetail.pinNumber': 934597,
  '2.registrationDetail.plateExpirationDate': '2023-06-06T00:00:00',
  '2.registrationDetail.plateIssueDate': '2013-12-06T00:00:00',
  '2.registrationDetail.possibleNgExemptionFlag': false,
  '2.registrationDetail.registrationNumber': 39287432,
  '2.registrationDetail.registrationOnlyFlag': '',
  '2.registrationDetail.registrationType': 'R',
  '2.registrationDetail.registrationUse': 'PR',
  '2.registrationDetail.renewalCountyCode': 12,
  '2.registrationDetail.rentalParkFlag': '',
  '2.registrationDetail.seminoleMiccosukeeIndianFlag': false,
  '2.registrationDetail.taxCollectorRenewalEligibilityFlag': true,
  '2.registrationDetail.vehicleClassCode': 1,
  '2.registrationOwners.0customer.addresses.0addressType': 'R',
  '2.registrationOwners.0customer.addresses.0city': 'SARASOTA',
  '2.registrationOwners.0customer.addresses.0countryCode': 'US',
  '2.registrationOwners.0customer.addresses.0countyCode': 12,
  '2.registrationOwners.0customer.addresses.0foreignPostalCode': '',
  '2.registrationOwners.0customer.addresses.0state': 'FL',
  '2.registrationOwners.0customer.addresses.0streetAddress': '39875 44TH DR E',
  '2.registrationOwners.0customer.addresses.0zipCode': 34243,
  '2.registrationOwners.0customer.addresses.0zipPlus': 5566,
  '2.registrationOwners.0customer.addresses.1addressType': 'M',
  '2.registrationOwners.0customer.addresses.1city': 'PALMETTO',
  '2.registrationOwners.0customer.addresses.1countryCode': 'US',
  '2.registrationOwners.0customer.addresses.1countyCode': 12,
  '2.registrationOwners.0customer.addresses.1foreignPostalCode': '',
  '2.registrationOwners.0customer.addresses.1state': 'FL',
  '2.registrationOwners.0customer.addresses.1streetAddress': '39875 44TH DR E',
  '2.registrationOwners.0customer.addresses.1zipCode': 34221,
  '2.registrationOwners.0customer.addresses.1zipPlus': '',
  '2.registrationOwners.0customer.companyName': '',
  '2.registrationOwners.0customer.customerNumber': 2398574,
  '2.registrationOwners.0customer.customerStopFlag': false,
  '2.registrationOwners.0customer.customerType': 'I',
  '2.registrationOwners.0customer.dateOfBirth': '1958-01-11T00:00:00',
  '2.registrationOwners.0customer.dlExpirationDate': '2020-01-11T00:00:00',
  '2.registrationOwners.0customer.dlRenewalEligibilityFlag': true,
  '2.registrationOwners.0customer.driverLicenseNumber': 'B23987433',
  '2.registrationOwners.0customer.emailAddress': '',
  '2.registrationOwners.0customer.feidNumber': '',
  '2.registrationOwners.0customer.firstName': 'DAVID2',
  '2.registrationOwners.0customer.lastName': 'HUGH2',
  '2.registrationOwners.0customer.middleName': 'BRIERTON2',
  '2.registrationOwners.0customer.militaryExemptionFlag': false,
  '2.registrationOwners.0customer.nameSuffix': '',
  '2.registrationOwners.0customer.sex': 'M',
  '2.registrationOwners.0registrationOwnershipNumber': 1,
  '2.shippingAddress.address.addressType': 'S',
  '2.shippingAddress.address.city': 'PALMETTO',
  '2.shippingAddress.address.countryCode': 'US',
  '2.shippingAddress.address.countyCode': 12,
  '2.shippingAddress.address.foreignPostalCode': '',
  '2.shippingAddress.address.state': 'FL',
  '2.shippingAddress.address.streetAddress': '293847 33TH ST W',
  '2.shippingAddress.address.zipCode': 34221,
  '2.shippingAddress.address.zipPlus': '',
  '2.shippingAddress.shippingCompanyName': '',
  '2.shippingAddress.shippingName1': 'DAVID2 HUGH2 BRIERTON2',
  '2.shippingAddress.shippingName2': '',
  '2.stops': '',
  '2.vehicle.address': '',
  '2.vehicle.bodyCode': '2D',
  '2.vehicle.brakeHorsePower': '',
  '2.vehicle.cubicCentimeters': '',
  '2.vehicle.grossWeight': '',
  '2.vehicle.insuranceAffidavitCode': '',
  '2.vehicle.leaseOwnerFlag': false,
  '2.vehicle.lengthFeet': '',
  '2.vehicle.lengthInches': '',
  '2.vehicle.majorColorCode': 'BLK',
  '2.vehicle.makeCode': 'PONT',
  '2.vehicle.minorColorCode': '',
  '2.vehicle.netWeight': 3802,
  '2.vehicle.numberOfAxles': '',
  '2.vehicle.ownerUnitNumber': '',
  '2.vehicle.titleNumber': 239857424,
  '2.vehicle.vehicleIdentificationNumber': '6G23242312UX63297437',
  '2.vehicle.vehicleNumber': '35T7843',
  '2.vehicle.vehicleType': 'AU',
  '2.vehicle.vesselCode': '',
  '2.vehicle.vesselManufacturerDesc': '',
  '2.vehicle.vesselResidentStatus': 'Y',
  '2.vehicle.vesselWaterType': '',
  '2.vehicle.widthFeet': '',
  '2.vehicle.widthInches': '',
  '2.vehicle.yearMake': 2006 ]

Upvotes: 10

Wolfgang
Wolfgang

Reputation: 896

Another solution:

function flatObjectToString(obj) {
    var path = [],
        nodes = {},
        parseObj = function (obj) {
            if (typeof obj == 'object') {
                if (obj instanceof Array) { //array
                    for (var i = 0, l = obj.length; i < l; i++) {
                        path.push(i);
                        parseObj(obj[i]);
                        path.pop();
                    }
                }
                else {  //object
                    for (var node in obj) {
                        path.push(node);
                        parseObj(obj[node]);
                        path.pop();
                    }
                }
            }
            else {  //value
                nodes[path.join('_')] = obj;
            }
        };

    parseObj(obj);
    return nodes;
}

console.log(JSON.stringify(flatObjectToString(data)));
  • I used a global path array to store the recursive path and prevent a lot a string operations
  • I don't know about the size of the actual JSON files so I tried to get a solution which doesn't waste any garbage
  • There is no need to check for "hasOwnProperty" in this (your) case

Upvotes: 3

Tarun Lalwani
Tarun Lalwani

Reputation: 146510

You can use something like below

data = require("./data.json")


flattenObject = (obj) => {
    let flattenKeys = {};
    for (let i in obj) {
        if (!obj.hasOwnProperty(i)) continue;
        if ((typeof obj[i]) == 'object') {
            // flattenKeys[i] = obj[i];
            let flatObject = flattenObject(obj[i]);
            for (let j in flatObject) {
                if (!flatObject.hasOwnProperty(j)) continue;
                flattenKeys[i + '.' + j] = flatObject[j];
            }
        } else {
            flattenKeys[i] = obj[i];
        }
    }
    return flattenKeys;
}

console.log(flattenObject(data))

The output on first element of your array object is as below

{ countyCode: 12,
  'customer.addresses.0.addressType': 'R',
  'customer.addresses.0.city': 'BRADENTON',
  'customer.addresses.0.countryCode': 'US',
  'customer.addresses.0.countyCode': 12,
  'customer.addresses.0.state': 'FL',
  'customer.addresses.0.streetAddress': '819 15th Ave Dr E',
  'customer.addresses.0.zipCode': 34211,
  'customer.addresses.1.addressType': 'M',
  'customer.addresses.1.city': 'BRADENTON',
  'customer.addresses.1.countryCode': 'US',
  'customer.addresses.1.countyCode': 12,
  'customer.addresses.1.state': 'FL',
  'customer.addresses.1.streetAddress': 'PO BOX 124',
  'customer.addresses.1.zipCode': 34201,
  'customer.addresses.1.zipPlus': '124',
  'customer.customerNumber': 932874,
  'customer.customerStopFlag': false,
  'customer.customerType': 'I',
  'customer.dateOfBirth': '1936-08-05T00:00:00',
  'customer.dlExpirationDate': '2022-08-05T00:00:00',
  'customer.dlRenewalEligibilityFlag': true,
  'customer.driverLicenseNumber': 'B360722339284',
  'customer.firstName': 'David',
  'customer.lastName': 'Brierton',
  'customer.middleName': 'Hugh',
  'customer.sex': 'M' }

Upvotes: 8

Related Questions