【问题标题】:Is there a simple way to transform this JSON back and forth?有没有一种简单的方法来来回转换这个 JSON?
【发布时间】:2019-07-21 00:41:02
【问题描述】:

我们正在使用 Postman 进行 API 测试。我们返回的一些对象非常冗长且不易处理,因此我想创建一个辅助方法以使它们更简洁。我知道有各种各样的转换库,如node-json-transform、selecttransform、jsontransforms 等,但不幸的是我只能使用Postman Sandbox libraries 和香草JS。

我正在寻找最简单(loc 和函数最少)的方法来转换这个对象:

var verbose = [
    {
        "Key": "Name",
        "Value": "John Doe",
        "Instance": 1
    },
    {
        "Key": "Age",
        "Value": "33",
        "Instance": 1
    },
    {
        "Key": "Child",
        "Value": "Jane",
        "Instance": 1
    },
    {
        "Key": "Child",
        "Value": "Rocky",
        "Instance": 2
    }];

进入这个:

var concise =  {
    "Name": "John Doe",
    "Age": "33",
    "Child": ["Jane", "Rocky"]
};

然后再回到详细形式。

我已经尝试过对每个对象进行 foreach 并将属性/值添加到新对象的本机方式,但是当我到达多个实例键/值对时,它很快就变得丑陋了。我可以想象使用 map/reduce 有一种更简单的方法,但我不熟悉这些方法。

【问题讨论】:

  • “我可以想象有一种更简单的方法使用 map/reduce,但我不熟悉这些方法。” - 然后熟悉它们。
  • 如何区分"foo": "bar" 和"foo": ["bar"]?或者您是否假设您永远不会拥有第二个版本,并且一旦看到一个键是多值的,就会自动将一个简单的值提升为一个数组值?
  • @Amadan 好问题。你的前提是正确的:这永远不会发生,如果是多值的,一个简单的值应该自动提升为一个数组。

标签: javascript json postman


【解决方案1】:

根据我对您问题的理解,您希望从 verbose 对象数组创建键值对。但是,如果存在键冲突,则应将值转换为数组。

考虑到这一点,您必须:

  1. 使用forEach 循环访问您的对象数组。
  2. 如果键不冲突,我们只需创建一个新的键值对
  3. 如果键冲突,那么它会变得有点棘手:
    • 如果键冲突并且这是第一次出现,我们将键值对中的值转换为数组
    • 如果键冲突并且这不是第一次出现,我们知道我们正在查看一个数组
    • 现在我们肯定有一个数组,所以我们将我们的值压入其中

请参阅下面的概念验证:

var verbose = [{
    "Key": "Name",
    "Value": "John Doe",
    "Instance": 1
  },
  {
    "Key": "Age",
    "Value": "33",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Jane",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Rocky",
    "Instance": 2
  }];

var concise = {};
verbose.forEach(function(i) {
  var key = i['Key'];
  var value = i['Value'];
  
  // If item exists, we want to convert the value into an array of values
  if (key in concise) {
    var item = concise[key];
    
    // If it is not an array already, we convert it to an array
    if (!Array.isArray(item))
      item = [item];
      
    item.push(value);
    concise[key] = item;
  }
  
  // If item does not exist, we simply create a new key-value pair
  else {
    concise[key] = value;
  }
});
console.log(concise);

【讨论】:

  • 这不是回答了一半的问题吗?
【解决方案2】:

在这里,我假设所有属性都是多值的,然后我将长度为 1 的属性减少为一个简单值。这比反向方法要慢一些,在反向方法中,您假设值是单值的,并在它们证明相反时将它们提升为数组,以尊重Instance 强加的排序。

function makeConcise(verbose) {
  let concise = {};
  verbose.forEach(({Key, Value, Instance}) => {
    if (!concise[Key]) concise[Key] = [];
    concise[Key][Instance - 1] = Value;
  });
  Object.keys(concise).forEach(Key => {
    if (concise[Key].length == 1) concise[Key] = concise[Key][0];
  });
  return concise;
}

反向函数同样简单:

function makeVerbose(concise) {
  let verbose = [];
  Object.keys(concise).forEach(Key => {
    if (Array.isArray(concise[Key])) {
      concise[Key].forEach((Value, index) => {
        verbose.push({Key, Value, Instance: index + 1});
      });
    } else {
      verbose.push({Key, Value: concise[Key], Instance: 1});
    }
  });
  return verbose;
}

【讨论】:

  • 我选择这个最能解决我的问题,因为它很短,对于 JS 开发新手来说一目了然,而且是最通用的(没有硬编码的键)。尽管如此,感谢大家花费宝贵的时间来解决这个问题(你会得到一个支持:-))。
【解决方案3】:
const verbose = [{
    "Key": "Name",
    "Value": "John Doe",
    "Instance": 1
  },
  {
    "Key": "Age",
    "Value": "33",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Jane",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Rocky",
    "Instance": 2
  }
];
let concise = {};

verbose.forEach(item => {
  const values = Object.values(item)
  if (concise[values[0]])  concise = {...concise, [values[0]]: [concise[values[0]], values[1]]};
  else concise = {...concise, ...{[values[0]]: values[1]}}
})

【讨论】:

  • 您将“赢得”最短的符号价格。不幸的是,Postman 不支持 {...consise, [values[0]]: 中的 ... 符号
【解决方案4】:

试试这个。我已经编写了两个转换函数。 我看到其他答案只提供了详细到简洁的要求。

let verbose = [{
    "Key": "Name",
    "Value": "John Doe",
    "Instance": 1
  },
  {
    "Key": "Age",
    "Value": "33",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Jane",
    "Instance": 1
  },
  {
    "Key": "Child",
    "Value": "Rocky",
    "Instance": 2
  }
]

let concise = {
  "Name": "John Doe",
  "Age": "33",
  "Child": ["Jane", "Rocky"]
}

verboseToConcise = (verbose) => {
  let obj = {}
  verbose.forEach(v => {
    let key = obj[v.Key]
    if (key) typeof key === 'string' ? obj[v.Key] = [key, v.Value] : key.push(v.Value)
    else obj[v.Key] = v.Value
  })
  return obj
}

conciseToVerbose = (concise) => {
  let arr = []
  Object.entries(concise).forEach(([key, value]) => {
    if (typeof value === 'object') {
      for (let i = 0; i < value.length; i++){
        arr.push({
          "Key": key,
          "Value": value[i],
          "Instance": i+1
        })
      }
    } else {
      arr.push({
        "Key": key,
        "Value": value,
        "Instance": 1
      })
    }
  })
  return arr
}

console.log(verboseToConcise(verbose))
console.log(conciseToVerbose(concise))

【讨论】:

    【解决方案5】:

    你可以这样做:

    const verbose = [{"Key": "Name","Value": "John Doe","Instance": 1},{"Key": "Age","Value": "33","Instance": 1},{"Key": "Child","Value": "Jane","Instance": 1},{"Key": "Child","Value": "Rocky","Instance": 2}];
    const concise = Object.values(verbose.reduce((a, {Key, Value}) => (Key === 'Child' ? a.childs[0].Child.push(Value) : a.keys.push({[Key]: Value}), a), {keys: [], childs: [{Child: []}]})).flat(1);
    
    console.log(concise);
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • @Amadan 更新了我的答案。谢谢
    【解决方案6】:

    我也尝试过使用reduce:

    编辑:不使用 ... 扩展语法,使用 Object.assign 和 array.concat

    EDIT2:我想再试一次。在这段代码中,我们丢失了Instance:(

    var verbose = [
      {
        Key: 'Name',
        Value: 'John Doe',
        Instance: 1,
      },
      {
        Key: 'Age',
        Value: '33',
        Instance: 1,
      },
      {
        Key: 'Child',
        Value: 'Jane',
        Instance: 1,
      },
      {
        Key: 'Child',
        Value: 'Rocky',
        Instance: 2,
      },
    ]
    
    const concise = verbose.reduce(
      (p, n) =>
        Object.assign(p, {
          [n.Key]: !p.hasOwnProperty(n.Key)
            ? n.Value
            : typeof p[n.Key] === 'string'
            ? [p[n.Key], n.Value]
            : p[n.Key].concat(n.Value),
        }),
      {},
    )
    
    console.log(concise)
    // { Name: 'John Doe', Age: '33', Child: [ 'Jane', 'Rocky' ] }
    
    const backAgain = Object.entries(concise).reduce(
      (p, [k, v]) =>
        Array.isArray(v)
          ? p.concat(v.map(x => ({ Key: k, Value: x })))
          : p.concat({ Key: k, Value: v }),
      [],
    )
    
    console.log(backAgain)
    // [ { Key: 'Name', Value: 'John Doe' },
    //  { Key: 'Age', Value: '33' },
    //  { Key: 'Child', Value: 'Jane' },
    //  { Key: 'Child', Value: 'Rocky' } ]
    

    【讨论】:

    • 很遗憾,Postman 不支持 {...,[n,Key]: 中的 ... 表示法
    • 谢谢,我更新了Object.assign 和array.concat 语法。
    猜你喜欢
    • 2015-04-15
    • 2019-12-27
    • 1970-01-01
    • 2022-11-10
    • 1970-01-01
    • 1970-01-01
    • 2012-06-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多