JSON.stringify自定义格式

时间:2016-08-08 15:04:23

标签: javascript json node.js

我正在寻找一种方法将JSON对象写入文件,但保持与orignal相同的格式。 我已经设法使用writeFileSync(路径,数据)和JSON.stringify()编写内容,但很难弄清楚如何自定义格式。 JSON.stringify接受的选项似乎只格式化空格数。

有没有办法使用JSON.stringify生成以下格式

{
  "key1"           :{"key": "value1","key": "value2"},
  "key2"           :{"key": "value1","key": "value2"}
}

而不是默认生成的

{
  "key1": 
   {
     "key": "value1",
     "key": "value2"
   },
  "key2": 
   {
     "key": "value1",
     "key": "value2"
   },
}

3 个答案:

答案 0 :(得分:1)

不幸的是,您可以使用space argument来定义对象落入树时的间距。您提议的内容需要使用正则表达式之类的自定义格式,以便在对字符串进行字符串化后对其进行格式化。

下面你会找到一些示例代码来做你想要做的事情。你可以将所有这些附加到像JSON.stringify(myJSON, null, ' ').replace(/: \{\n\s+/g, ': {').replace(/",\n\s+/g, ', ').replace(/"\n\s+\}/g, '}');这样的单个命令中,但是你可以看到我做了什么,我一步一步地将它分解。

const myJSON = {
  "key1":{"key": "value1","key2": "value2"},
  "key2":{"key": "value1","key2": "value2"}
}

let myString = JSON.stringify(myJSON, null, ' ').replace(/: {/g, `${' '.repeat(5)}: \{`); //Set the key spacing
myString = myString.replace(/: \{\n\s+/g, ': {'); //Bring the child bracket on the same line
myString = myString.replace(/",\n\s+/g, ', '); //Bring all the other objects for that array up
myString = myString.replace(/"\n\s+\}/g, '}'); //Pull the closing bracket on the same line

const myCompactString = JSON.stringify(myJSON, null, ' ').replace(/: {/g, `${' '.repeat(5)}: \{`).replace(/: \{\n\s+/g, ': {').replace(/",\n\s+/g, ', ').replace(/"\n\s+\}/g, '}'); //Done all at once

console.log(`myString: ${myString}`);
console.log(`myCompactString: ${myCompactString}`);

答案 1 :(得分:0)

我想要这样的格式:

var house = {
    family: {
        surname: "Smith",
        people: 4,
        pets: {
            dogs: {number:1, names:["toby"]},
            cats: {number:2, names:["bob", "boo"]},
            platypus: {number:1, names:["perry"], codename: ["agent p"]},
        }
    },
    livingRoom: [
        {name:"couch", amount:2},
        {name:"shelf", amount:1},
        {name:"nightstand", amount:1},
        {name:"television", amount:1},
    ],
    bedroom: [
        {name:"bed", amount:1},
        {name:"wardrobe", amount:1},
        {name:"shelf", amount:2},
    ],
}

这是一个类似的问题,但是在更复杂的对象树中具有更具体的对象键,并且仅使用正则表达式就开始变得相当复杂。因此,我使用toJSONstringify replacer作为处理此问题的工具创建了一个函数。

我创建的函数有一些限制,例如只能定义singleLine和multiLine对象,而不是数字或文本列表。它还具有用于替换方案的某些特定字符的限制,它们是不寻常的字符,但是请确认是否必须替换它们。字符有四个:“╲”(假\),“””(假“”),“→”和“←”,如果对象包含其中的任何一个,则可以在函数的开头替换它们。

下面是一个功能正常的示例:

const customStringify = function (obj, replacer, space = "\t", { singlelineObjectKeys = [], multilineObjectKeys = [], singlelineChildKeys = [], multilineChildKeys = [], singlelineInsideList = [] }) {

    // WARNING
    // - This function will make a mess if your Object contain some of the following characters:
    const fakeReverseSlash = "╲"; // (fake \)
    const fakeQuote = "”"; // (fake ")
    const startString = "→";
    const endString = "←";
    // So a solution in this case can be replace this chars by others not used (dont use characters that can mess the regex)

    // First make a stringify to solve any toJSON in the main object, then copy the main object stringfied to create all the necessary new toJSON
    let objModified = JSON.parse(JSON.stringify(obj, replacer));

    // Convert an entire object to single line string
    const singleLineOBJ = function () {
        let obj = { ...this }; // To not change any toJSON
        delete obj.toJSON // To not fall in a stringify loop

        // Mark the startString and endString
        return startString + (
            JSON.stringify(obj)
                // Replace all " by fakeQuote
                .replace(/"/g, fakeQuote)
        ) + endString;
    }

    // Convert an entire object to multi line string
    const multiLineOBJ = function () {
        let obj = { ...this }; // To not change any toJSON
        delete obj.toJSON // To not fall in a stringify loop

        // Mark the startString and endString
        return startString + (
            JSON.stringify(obj, null, '\t')
                // Replace all " by fakeQuote
                .replace(/"/g, fakeQuote)
                // Replace \n using fakeReverseSlash
                .replace(/\n/g, fakeReverseSlash + "n")
                // Replace \t using fakeReverseSlash
                .replace(/\t/g, fakeReverseSlash + "t")
        ) + endString;
    }

    // Checks all keys on the object
    const throughEveryKey = function (key, value) {
        let obj = this;
        // objects with some key to become single-line
        if (singlelineObjectKeys.includes(key)) {
            obj[key].toJSON = singleLineOBJ;
        }
        // objects with some key to become multi-line
        if (multilineObjectKeys.includes(key)) {
            obj[key].toJSON = multiLineOBJ;
        }
        // objects containing the following keys to become single-line
        if (singlelineChildKeys.includes(key)) {
            obj.toJSON = singleLineOBJ;
        }
        // objects containing the following keys to become multi-line
        if (multilineChildKeys.includes(key)) {
            obj.toJSON = multiLineOBJ;
        }
        // names of list of objects to each list-item become single-line
        if (singlelineInsideList.includes(key)) {
            obj[key].forEach((objectInsideList) => objectInsideList.toJSON = singleLineOBJ)
        }
        return value;
    }

    // Just use stringify to go through all object keys, and apply the function to implement "toJSON" in right places, the result of stringify is not used in this case
    JSON.stringify(objModified, throughEveryKey);

    // Use stringfy with right replacers, end result
    return JSON.stringify(objModified, null, '\t')
        // Put in all start of line the right number of Tabs
        .replace(new RegExp('(?:(?<=(?<leadTab>^\t*).+?)(?<newLine>' + fakeReverseSlash + 'n)(?=.+?))+', 'gm'), "$&$1")
        // Replace the fake tab by the real one
        .replace(new RegExp(fakeReverseSlash + 't', 'gm'), "\t")
        // Replace the fake new line by the real one
        .replace(new RegExp(fakeReverseSlash + 'n', 'gm'), "\n")
        // Replace the fake quote by the real one
        .replace(new RegExp(fakeQuote, 'gm'), '"')
        // Remove start and end of line from the stringfied object
        .replace(new RegExp('"' + startString, 'gm'), "")
        .replace(new RegExp(endString + '"', 'gm'), "")
        // Replace tab by custom space
        .replace(/(?<=^\t*)\t/gm, space);

}

var house = {
    family: {
        surname: "Smith",
        people: 4,
        pets: {
            dogs: {number:1, names:["toby"]},
            cats: {number:2, names:["bob", "boo"]},
            platypus: {number:1, names:["perry"], codename: ["agent p"]},
        }
    },
    livingRoom: [
        {name:"couch", amount:2},
        {name:"shelf", amount:1},
        {name:"nightstand", amount:1},
        {name:"television", amount:1},
    ],
    bedroom: [
        {name:"bed", amount:1},
        {name:"wardrobe", amount:1},
        {name:"shelf", amount:2},
    ],
}

console.log("A custom stringify:\n\n");
console.log(

customStringify(house, null, '\t', {
    singlelineObjectKeys: ["dogs","cats","platypus"],
    multilineObjectKeys: ["family"],
    multilineChildKeys: [],
    singlelineChildKeys: [],
    singlelineInsideList: ["livingRoom","bedroom"],
})

);

console.log("\n\n\n");

console.log("A normal stringify:\n\n");
console.log(JSON.stringify(house, null, '\t'));

您需要传递一些信息才能工作,您不需要使用所有内容,但是我将解释我准备在multiLine和singleLine之间设置格式的情况:

singlelineObjectKeys::将需要单行对象的键放在此处

multilineObjectKeys::在此处放置需要多行格式化的对象的键

multilineChildKeys::如果您不想指定父对象,因为它们可能有很多,请为要多行格式化的父对象指定子键

singlelineChildKeys::如果您不想指定父对象,因为它们可能有很多,请为要在一行上设置格式的父对象指定一个子键

singlelineInsideList::如果您有一个对象列表,并且希望将该列表中的所有对象格式化为一个列表,请将该列表的键放在此处

在上面的示例中,使用的代码是这样的:

customStringify(house, null, '\t', {
    singlelineObjectKeys: ["dogs","cats","platypus"],
    multilineObjectKeys: ["family"],
    singlelineInsideList: ["livingRoom","bedroom"],
})

但这会产生相同的结果,例如:

customStringify(house, null, '\t', {
    singlelineChildKeys: ["name","names"],
})

由于我在多个地方进行了研究,但对此一无所获,因此我在这里注册了解决方案(这是我寻找的地方之一)。随时使用!

答案 2 :(得分:-1)

以 KevynTD 的出色回答为基础!

防止字符串数组 ['song', 'bird'] 更改为 {'0': 'song', '1': 'bird'} 索引键控对象。将 if (Array.isArray(this)) obj = this; 添加到 singleLineOBJ 函数中。

    const singleLineOBJ = function () {
        let obj = { ...this }; // To not change any toJSON
        if (Array.isArray(this))
            obj = this; // override converting array into object

        delete obj.toJSON // To not fall in a stringify loop