Json - stringify so that arrays are on one line

Viewed 22950

Is it possible to stringify a JSON object to look like this, with arrays in one line - not indented

{
    "Repeat": {
        "Name": [["Top_level","All"],[[1,1]]],
        "Link": [["Top_level"],[[1,1]]]
    },
    "Delete": ["Confirm","Cancel"],
    "Move": ["Up","Down"],
    "Number": ["Ascending","Descending"]
}
10 Answers

Taking the answers from ericbowden and bigp, I produced the below function which allows me to pretty print JSON while keeping an array on a single line and keeping the array in array form instead of converting it to a string.

function prettyPrintArray(json) {
  if (typeof json === 'string') {
    json = JSON.parse(json);
  }
  output = JSON.stringify(json, function(k,v) {
    if(v instanceof Array)
      return JSON.stringify(v);
    return v;
  }, 2).replace(/\\/g, '')
        .replace(/\"\[/g, '[')
        .replace(/\]\"/g,']')
        .replace(/\"\{/g, '{')
        .replace(/\}\"/g,'}');

  return output;
}

Another approach which I took:

obj => JSON.stringify(obj, (k,v) => Array.isArray(v) ? JSON.stringify(v) : v, 2)
.replace(/"\[[^"\]]*]"/g, r => JSON.stringify(JSON.parse(r)).substr(1).slice(0,-1))

*Array must not contain strings (notice the " in the not contains inside the regex), if you remove it it will catch key,values of: "[": "[1,2,3,4]",

Update 2020-03 - I worked out a more stable solution

const obj = {
  "first_name": "John",
  "last_name": "Smith",
  "age": 21,
  "hobbies": [ "programming", "workout", null, undefined, 24, "\"has double quotes\"" ],
  "nested": {
    "arr": [ "one", "two", "three" ],
  },
  "nested_arr": [
    "first as string",
    {
      "latin": [ "alpha", "beta", "[gamma]" ]
    },
    null
  ]
};

const stringify = (obj, indent = 2) => 
  JSON.stringify(obj, (key, value) => {
    if (Array.isArray(value) && !value.some(x => x && typeof x === 'object')) {
      return `\uE000${JSON.stringify(value.map(v => typeof v === 'string' ? v.replace(/"/g, '\uE001') : v))}\uE000`;
    }
    return value;
  }, indent).replace(/"\uE000([^\uE000]+)\uE000"/g, match => match.substr(2, match.length - 4).replace(/\\"/g, '"').replace(/\uE001/g, '\\\"'));

console.log(stringify(obj));

Another modern answer for an old question: Take a look at FracturedJson. That link will take you to the web version, but it's available as a commandline app, and libraries for .NET and JS.

FracturedJson will inline arrays/objects as long as they're neither too long nor too complex. It can likewise split arrays into multiple lines with multiple items per line.

Here's an example using the default settings, but you can adjust them to whatever works best for your data.

{
    "SimpleItem": 77,
    "ComplexObject": {
        "Subthing1": {"X": 55, "Y": 19, "Z": -4},
        "Subthing2": { "Q": null, "W": [-2, -1, 0, 1] },
        "Distraction": [[], null, null]
    },
    "ShortArray": ["blue", "blue", "orange", "gray"],
    "LongArray": [
        2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 
        79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 
        163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 
        251, 257, 263, 269, 271, 277, 281, 283, 293
    ],
    "LongArray2": [
        [19, 2],
        [3, 8],
        [14, 0],
        [9, 9],
        [9, 9],
        [0, 3],
        [10, 1],
        [9, 1],
        [9, 2],
        [6, 13],
        [18, 5],
        [4, 11],
        [12, 2]
    ]
}

Disclosure: I'm the author of FracturedJson. It's open source under an MIT license.

I use JSON format for my app's configuration files. They are quite dissimilar and big enough, so different formatting rules are required to make them look better and readable. Unfortunately, provided answers are not flexible enough so I've made my own implementation called perfect-json to beautify JSON.

Consider that you want to format object in the question like this:

{
  "Repeat": {
    "Name": [
      ["Top_level", "All"],
      [[1, 1]]
    ],
    "Link": [
      ["Top_level"],
      [[1, 1]]
    ]
  },
  "Delete": ["Confirm", "Cancel"],
  "Move": ["Up", "Down"],
  "Number": [
    "Ascending",
    "Descending"
  ]
}

With perfect-json it can be achieved:

import perfectJson from 'perfect-json';

const obj = {
  Repeat: {
    Name: [['Top_level', 'All'], [[1, 1]]],
    Link: [['Top_level'], [[1, 1]]]
  },
  Delete: ['Confirm', 'Cancel'],
  Move: ['Up', 'Down'],
  Number: ['Ascending', 'Descending']
};

console.log(perfectJson(obj, {
  singleLine: ({ key, path, depth }) => {
    if (['Delete', 'Move'].includes(key)) {
      return true;
    }
    if (depth >= 3 && ['Name', 'Link'].includes(path[1])) {
      return true;
    }
    return false;
  }
}));

It's also easy to place each array on a single line as asked in the question:

console.log(perfectJson(obj, {
  indent: 4,
  singleLine: ({ value }) => Array.isArray(value)
}));

I use prettier.

First install it:

npm i prettier

Then use it:

const prettier = require("prettier")

const obj = {"Repeat": {"Name":[["Top_level","All"],[[1,1]]],"Link": [["Top_level"],[[1,1]]]},"Delete": ["Confirm","Cancel"],"Move": ["Up","Down"],"Number": ["Ascending","Descending"]};

const json = prettier.format(JSON.stringify(obj), { parser: "json" });
Related