如何从 JSON 兼容的嵌套 object/data-structure 中递归生成类似标记的字符串?

How does one recursively generate a markup-like string from a JSON-compatible nested object/data-structure?

let json =  {
    "user": {
        "first_name": "test",
        "second_name": "second name",
        "profile": {
            "test":{
                "img": "img"
            }      
        }
    },
    "company": {
        "name": "company 1",
        "company_nested": {
            "test" : "test"
        }
    }
}
let new_json = "";

function iterate(obj) {
    Object.keys(obj).forEach((key, index) => {
        let value = obj[key];
        if(typeof value === 'string') {         
            new_json += `"${key}": ${value}\n`;         
        }

        if (typeof value === 'object') {     
            new_json +=`##${key}##\n`;
            iterate(value);     
        }                   
    });
}
    
iterate(json);
$("#teste").html(new_json);

结果

##user##
"first_name": test
"second_name": second name
##profile##
##test##
"img": img

有没有更好的方法来更改我想要的东西(自定义的)的大括号?

期望的结果

##user##
"first_name": test
"second_name": second name
##profile##
##test##
"img": img
##/test##
##/profile##
##/user##

##company##
"name": company 1
##company_nested##
"test": test
##/company_nested##
##/company##

您可以遍历对象的条目。如果 values 是一个对象,则递归调用函数并将对象包装在 ##${k}####/${k}##

const input={user:{first_name:"test",second_name:"second name",profile:{test:{img:"img"}}},company:{name:"company 1",company_nested:{test:"test"}}};

function replace(o) {
  return Object.entries(o).map(([k, v]) => {
    if (typeof v === 'object')
      return `##${k}##
${replace(v)}
##/${k}##`
    else
      return `"${k}": ${v}`
  }).join("\n")
}

console.log(replace(input))

如果多行模板文字难以阅读,您可以创建一个数组并使用 join:

const replace = o =>
  Object.entries(o).map(([k, v]) =>
      typeof v === 'object' 
          ? [`##${k}##`, replace(v), `##/${k}##`].join('\n') 
          : `"${k}": ${v}`
  ).join("\n")

我假设您的 json 格式正确。如果没有,您可能需要在代码中处理。

第 1 步:将您的 json 转换为字符串。

let str = JSON.stringify(json);

第 2 步:创建一个符号映射,将现有符号作为键,将新符号作为值。

let symbolMap = new Map();
symbolMap.set("{", "$");
symbolMap.set("}", "%");

迭代字符串中的每个字符,检查字符是否存在于映射中,如果存在,则替换新值。

for (let i = 0; i < str.length; i++) {
  if (symbolMap.get(str[i])) {
    str = str.replace(str[i], symbolMap.get(str[i]));
  }
}

打印输出

console.log(str);

输出: 之前:

{"user":{"first_name":"test","second_name":"second name","profile":{"test":{"img":"img"}}},"company":{"name":"company 1","company_nested":{"test":"test"}}}

之后:

$"user":$"first_name":"test","second_name":"second name","profile":$"test":$"img":"img"%%%,"company":$"name":"company 1","company_nested":$"test":"test"%%%

完整代码:

let json = {
  user: {
    first_name: "test",
    second_name: "second name",
    profile: {
      test: {
        img: "img",
      },
    },
  },
  company: {
    name: "company 1",
    company_nested: {
      test: "test",
    },
  },
};

let str = JSON.stringify(json);

let symbolMap = new Map();
symbolMap.set("{", "$");
symbolMap.set("}", "%");

console.log(str);

for (let i = 0; i < str.length; i++) {
  if (symbolMap.get(str[i])) {
    str = str.replace(str[i], symbolMap.get(str[i]));
  }
}

console.log(str);


此方法基于单个递归调用的 reducer 函数。它也是一个通用的标记生成解决方案,因为它可以通过 reducer 函数的第一个参数进行配置 ...

function createCustomMarkupRecursively(collector, [key, value], idx) {
  collector.config = collector.config || {};
  collector.level = collector.level || 0;

  const {
    markup = '',
    config: {
      quote = '"',
      prefix = '##', suffix = '##',
      terminator = '/', separator = '\n',
    },
  } = collector;

  const markupBlockSeparator = ((collector.level === 0) && (idx > 0))
    ? separator
    : '';

  if (value && (typeof value === 'object')) {
    ++collector.level;

    collector.markup = [
      markup,
      markupBlockSeparator,
      `${ prefix }${ key }${ suffix }\n`,
      Object.entries(value).reduce(
        createCustomMarkupRecursively,
        Object.assign(collector, { markup: ''}),
      ).markup,
      `${ prefix }${ terminator }${ key }${ suffix }\n`,
    ].join('');

    --collector.level;
  } else {
    collector.markup = [
      markup,
      markupBlockSeparator,
      quote,
      key,
      quote,
      `: ${ value }\n`,
    ].join('');
  }

  return collector;
}

const data =  {
  user: {
    first_name: "test",
    second_name: "second name",
    profile: {
      test: {
        img: "img",
      },     
    },
  },
  company: {
    name: "company 1",
    company_nested: {
      test : "test",
    },
  },
};
const { markup } = Object
  .entries(data)
  .reduce(createCustomMarkupRecursively, {/*
    config: {
      quote: '"',
      prefix: '##',
      suffix: '##',
      terminator: '/',
      separator: '\n'
    },*/
    markup: '',
  });

// according to the OP ...
console.log(markup);

// customization sample ...
console.log(
  Object
  .entries(data)
  .reduce(createCustomMarkupRecursively, {

    config: {
      quote: "'",
      prefix: '<',
      suffix: '>',
      terminator: '$',
      separator: '\n<-- // -->\n\n',
    },
    markup: '## another markup configuration result ##\n\n',

  }).markup
)
.as-console-wrapper { min-height: 100%!important; top: 0; }