如何在JavaScript中过滤嵌套数据结构
假设我有一个嵌套的JavaScript对象,如下所示:如何在JavaScript中过滤嵌套数据结构,javascript,json,Javascript,Json,假设我有一个嵌套的JavaScript对象,如下所示: { "?xml": { "@version": "1.0", "@encoding": "UTF-8" }, "Customer": { "@xmlns": "http://NamespaceTest.com/CustomerTypes", "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "Name": {
{
"?xml": {
"@version": "1.0",
"@encoding": "UTF-8"
},
"Customer": {
"@xmlns": "http://NamespaceTest.com/CustomerTypes",
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"Name": {
"#text": "Name1"
},
"DeliveryAddress": {
"Line1": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line11"
},
"Line2": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line21"
}
}
}
}
function replacer(key, value) {
if (key === "?xml" || key === "@xmlns") {
return undefined;
}
return value;
}
var filtered = JSON.parse( JSON.stringify( original, replacer ) );
function replacer (key, value) {
if (key === "?xml" || key === "@xmlns") return undefined;
else return value;
}
var filtered = filterClone(original, replacer);
function replacer (key, value) {
// this is how you clone a date in JS:
if (value instanceof Date) value = new Date(value.getTime());
return value;
}
我想按名称定义属性列表,例如,[“?xml”、“@xmlns”]
,并从结构中删除这些属性,以便获得以下输出:
{
"Customer": {
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"Name": {
"#text": "Name1"
},
"DeliveryAddress": {
"Line1": {
"#text": "Line11"
},
"Line2": {
"#text": "Line21"
}
}
}
}
我知道我可以用这样的方法来做:
{
"?xml": {
"@version": "1.0",
"@encoding": "UTF-8"
},
"Customer": {
"@xmlns": "http://NamespaceTest.com/CustomerTypes",
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"Name": {
"#text": "Name1"
},
"DeliveryAddress": {
"Line1": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line11"
},
"Line2": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line21"
}
}
}
}
function replacer(key, value) {
if (key === "?xml" || key === "@xmlns") {
return undefined;
}
return value;
}
var filtered = JSON.parse( JSON.stringify( original, replacer ) );
function replacer (key, value) {
if (key === "?xml" || key === "@xmlns") return undefined;
else return value;
}
var filtered = filterClone(original, replacer);
function replacer (key, value) {
// this is how you clone a date in JS:
if (value instanceof Date) value = new Date(value.getTime());
return value;
}
但我不喜欢先将结果转换为字符串,然后再解析回对象。是否有一个函数可以过滤像JSON这样的数据结构。stringify()可以,但它返回的是一个对象而不是字符串?这里有一个解决方案
var json='{?xml:{@version:“1.0”,“@encoding:“UTF-8”},“Customer:{@xmlns:”http://NamespaceTest.com/CustomerTypes“,@xmlns:xsi”:”http://www.w3.org/2001/XMLSchema-instance“,”名称“:{”#文本“:”名称1“},“交货地址“{”Line1“:{”@xmlns:”http://NamespaceTest.com/CommonTypes“,“#text”:“Line11”},“Line2”:{“@xmlns”:"http://NamespaceTest.com/CommonTypes“,”文本“:“Line21”}}}}”;
var obj=JSON.parse(JSON);
函数RemoveNameSpace(_obj){
var _this=_obj;
对于(此中的var p){
if(p==“?xml”| p==“@xmlns”){
删除此[p];
}
if(typeof(_this[p])==“object”){
RemoveNameSpace(_this[p])
}
}
把这个还给你;
}
var newjson=JSON.stringify(RemoveNameSpace(obj));
console.log(newjson);
据我所知,JavaScript中没有内置的方法来深度过滤嵌套的数据结构,就像在给定替换回调时一样。也就是说,编写自己的代码并不难:
function filterClone (data, replacer) {
// return primitives unchanged
if ( !(data instanceof Object) ) return data;
// don't try to clone anything except plain objects and arrays
var proto = Object.getPrototypeOf(data);
if (proto !== Object.prototype && proto !== Array.prototype) return data;
// it's a "plain object" or an array; clone and filter it!
var clone = (proto === Object.prototype ? {} : []);
for (var prop in data) {
// safety: ignore inherited properties, even if they're enumerable
if (!data.hasOwnProperty(prop)) continue;
// call the replacer to let it modify or exclude the property
var value = replacer(prop, data[prop]);
if (value === undefined) continue;
if (value instanceof Object) value = filterClone(value, replacer);
clone[prop] = value;
}
return clone;
}
上面的递归函数将深度克隆任何“类似JSON”的数据结构(即仅由普通的{}
对象、[]
数组和数字、字符串和布尔等基本类型组成的数据结构),并使用与JSON.stringify()完全相同的替换回调对其进行过滤
。也就是说,如您所问,给定一个类似JSON的对象original
,您可以创建一个过滤后的副本,如下所示:
{
"?xml": {
"@version": "1.0",
"@encoding": "UTF-8"
},
"Customer": {
"@xmlns": "http://NamespaceTest.com/CustomerTypes",
"@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
"Name": {
"#text": "Name1"
},
"DeliveryAddress": {
"Line1": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line11"
},
"Line2": {
"@xmlns": "http://NamespaceTest.com/CommonTypes",
"#text": "Line21"
}
}
}
}
function replacer(key, value) {
if (key === "?xml" || key === "@xmlns") {
return undefined;
}
return value;
}
var filtered = JSON.parse( JSON.stringify( original, replacer ) );
function replacer (key, value) {
if (key === "?xml" || key === "@xmlns") return undefined;
else return value;
}
var filtered = filterClone(original, replacer);
function replacer (key, value) {
// this is how you clone a date in JS:
if (value instanceof Date) value = new Date(value.getTime());
return value;
}
请注意,此函数创建的“深度克隆”并不完美(因为),并且有一些极端情况需要注意:
- 此函数仅克隆直接继承自
或对象
(包括“普通对象”和使用数组
和{}
创建的数组)的对象。其他任何对象,包括基元值和任何其他类型的对象,都只需复制到输出结构中,而无需克隆 对于基元值来说,这是无害的,因为它们无论如何都是不可变的;但是如果您的数据结构碰巧包含(比如)对象(它们是可变的),那么这些对象将不会被自动克隆。因此,修改克隆数据结构中的日期(使用例如[]
)可能会影响原件中的日期,反之亦然:setTime()
当然,您可以在replacer回调中解决此问题,例如:var original = { "date" : new Date("1970-01-01T00:00:00.000Z") }; var clone = filterClone( original, function (key, val) { return val } ); console.log( original === clone ); // -> false console.log( original.date === clone.date ); // -> true (!) console.log( original.date.getTime() ); // -> 0 clone.date.setYear(2016); console.log( original.date.getTime() ); // -> 1451606400000
{ "?xml": { "@version": "1.0", "@encoding": "UTF-8" }, "Customer": { "@xmlns": "http://NamespaceTest.com/CustomerTypes", "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "Name": { "#text": "Name1" }, "DeliveryAddress": { "Line1": { "@xmlns": "http://NamespaceTest.com/CommonTypes", "#text": "Line11" }, "Line2": { "@xmlns": "http://NamespaceTest.com/CommonTypes", "#text": "Line21" } } } }
function replacer(key, value) { if (key === "?xml" || key === "@xmlns") { return undefined; } return value; } var filtered = JSON.parse( JSON.stringify( original, replacer ) );
function replacer (key, value) { if (key === "?xml" || key === "@xmlns") return undefined; else return value; } var filtered = filterClone(original, replacer);
function replacer (key, value) { // this is how you clone a date in JS: if (value instanceof Date) value = new Date(value.getTime()); return value; }
- 此外,上面的
函数不会克隆对象中的任何不可枚举属性,克隆中的任何非标准(可枚举)属性将替换为标准属性(没有getter、setter、write restrictions等)object literal语法不应该有任何这样奇特的属性描述符,但如果您在之后添加了任何属性描述符,请注意它们将不会被克隆(显然,也不会被克隆)filterClone()
- 如果原始对象两次包含对同一普通对象或数组的引用,它们将在克隆中成为单独的对象/数组。例如:
var sharedObject = {}; var original = { "foo" : sharedObject, "bar" : sharedObject }; var clone = filterClone( original, function (key, val) { return val } ); console.log( original.foo === original.bar ); // -> true console.log( clone.foo === clone.bar ); // -> false
- 此外,如果您的对象不是(例如,如果它们包含对自身的引用),
可能永远陷入无限递归中(或者直到它到达目标)。例如,这里有一个简单的方法来创建一个无法使用filterClone()
:filterClone()克隆的对象。
- 最后,由于
replacer回调接口(上面的代码忠实地遵循该接口)将JSON.stringify()
用作特殊值,表示“忽略此属性”,因此无法使用undefined
。克隆filterClone()正确克隆包含
值的对象undefined
或false
值可以正常工作,不过:null
(然而,在测试时,我确实在我的原始实现中发现了一个bug:显然,var original = { "foo" : undefined, "bar" : null }; var clone = filterClone( original, function (key, val) { return val } ); console.log( clone ); // -> Object { bar: null }
抛出了一个类型错误。在原型检查之前移动Object.getPrototypeOf(null)
检查解决了这个问题。)instanceof
JSON.parse(JSON.stringify(obj))
。如上所述,深入克隆任意对象是很困难的,特别是在JavaScript这样的语言中,它没有标准的方法将对象标记为可克隆的,并且允许对象包含各种奇怪的属性非常灵活。不过,对于“简单”对象(尤其是通过解析有效JSON字符串返回的任何内容),此函数应该可以完成此操作
当然,回避这些问题的一种方法是在适当的地方进行过滤:
function filterInplace (data, replacer) {
// don't try to filter anything except plain objects and arrays
if ( !(data instanceof Object) ) return;
var proto = Object.getPrototypeOf(data);
if (proto !== Object.prototype && proto !== Array.prototype) return;
// it's a "plain object" or an array; filter it!
for (var prop in data) {
// safety: ignore inherited properties, even if they're enumerable
if (!data.hasOwnProperty(prop)) continue;
// call the replacer to let it modify or exclude the property
data[prop] = replacer(prop, data[prop]);
if (data[prop] instanceof Object) filterInplace(data[prop], replacer);
if (data[prop] === undefined) delete data[prop];
}
}
此函数不返回任何内容;相反,它只是修改作为第一个参数传入的数据结构。它确实有一些独特之处:
- 除了“普通”对象和数组,它甚至不尝试过滤任何东西
- 它仍然会在没有良好基础的结构上中断(就像任何没有显式跟踪它已经访问过的对象的递归解决方案一样)
- 出于同样的原因,如果同一对象从数据结构中被引用两次,它将被过滤两次。(但是,由于不涉及克隆,这两个引用将一直指向同一对象。)
- 它仍然不能正确地过滤不可枚举的数据