【问题标题】:JSON.stringify() losing nested properties I know the cause but don't know the answerJSON.stringify() 丢失嵌套属性 我知道原因但不知道答案
【发布时间】:2020-08-29 16:27:16
【问题描述】:

我有 2 个对象:

const subscription = {
 endpoint: "dfksjfklsjkld",
 keys: {
    pkey: "dfsfsdfsf",
    auth: "dfsdfsdfsd"
 }
};

const extra = {
  email: "dfsdfs",
  ip:"231342.342.342.34"
};

我想把 extra 对象放在订阅中,所以它看起来像:

subsciption = {
 endpoint: ......
 keys: {...},
 extra: {
    email:....,
    ip: .....
 }
}

然后我需要将其作为 http 请求的正文发送:

const response = await fetch(url, {
  method: "PUT", 
  mode: "no-cors",
  cache: "no-cache",
  credentials: "same-origin", 
  headers: {
    "Content-Type": "application/json",
  },
  redirect: "follow", 
  referrerPolicy: "no-referrer", 
  body: JSON.stringify(subscription), 
});

但是我发现不管我做什么,我总是在JSON.stringify()的过程中丢失订阅里面的额外属性。

我知道原因:这是因为额外对象中的属性不可枚举。

到目前为止,我已经尝试过:

1.使用传播:

newSub = {
  ...subscription,
  ...extra
}

但是newSub的内容会和extra完全一样,subscription的属性都丢失了。

2.将toJSON函数添加到我生成额外对象的地方

getExtra() : {    
.......
return {
      city: ipObject.city,
      country: ipObject.country_name,
      ip: ipObject.ip,
      lat: ipObject.latitude,
      lng: ipObject.longitude,
      org: ipObject.org,
      postal: ipObject.postal,
      region: ipObject.region,
      toJSON: () => {
        return this;
      }
    };    
}

完全没有效果。

我在这里附上我的代码:

async function updateSubscription() {
  try {
    const allowed = await askForPermission();
    if (!allowed) return;

    let subscription = await getSubscription();
    if (!subscription) return;

    // email
    const email = getEmail();
    if (!email || !validateEmail(email)) {
      alert("huh...so how are you going to receive notifications?");
      return;
    }

    // ip
    let ipObject = await getIP();
    let extra = {};
    if (ipObject) {
      ipObject.email = email;
      extra = ipObject;
    } else {
      extra.email = email;
    }

    console.log("extra: ", extra);

    // var newSubscription = Object.assign({}, subscription, {extra});
    // const newSubscription = {
    //   ...subscription,
    //   extra
    // };
    let newSubscription = subscription;
    newSubscription["extra"] = extra;
    console.log("new subscription1: ", newSubscription);
    console.log("new subscription1 stringified: ", JSON.stringify(newSubscription));

    const successful = await saveRegistration(newSubscription);

    if (successful) alert("you have successfully subscribed to the DC monitor");
    else alert("shit happens, try it later");
  } catch (err) {
    console.log("updateSubscription() failed: ", err);
  }
}

async function getSubscription() {
  console.log("try to get subscription");
  try {
    const swRegistration = await navigator.serviceWorker.ready;
    const pushSubscription = await swRegistration.pushManager.getSubscription();
    console.log("pushSubscription: ", pushSubscription);
    return pushSubscription;
  } catch (error) {
    console.log("getSubscription() error: ", error);
    return null;
  }
}

更新

1.尝试了另外一种方法:

var newSubscription = Object.assign({}, subscription, {extra});
console.log("subscription: ", newSubscription);
console.log("subscription stringified: ", JSON.stringify(newSubscription));

这是输出截图:

2.还有这个:

 const newSubscription = {
      ...subscription,
      extra
    };
    console.log("new subscription: ", newSubscription);
    console.log("new subscription stringified: ", JSON.stringify(newSubscription));

这是输出的截图:

3.用字符串索引方式:

let newSubscription = subscription;
    newSubscription["extra"] = extra;
    console.log("new subscription1: ", newSubscription);
    console.log("new subscription1 stringified: ", JSON.stringify(newSubscription));

【问题讨论】:

  • 为什么额外的属性不可枚举?它是一种什么样的特殊物体?
  • subscriptionObject 是什么?
  • subscriptionObject 设置在哪里?
  • 啊,真不错
  • 也许从该对象显式复制您需要的属性?由于它是一个宿主对象,因此您无法对它的实现方式做出任何假设(我认为)。根据规范,它实际上实现了自己的 JSON 序列化过程。所以也许你想改用sub = subscription.toJSON(); sub.extra = extra; JSON.stringify(sub)

标签: javascript json stringify


【解决方案1】:

如果改变subscription 是可以的,你可以使用:

subscription['extra'] = extra;

如果你想要一个新的对象,你可以使用:

const subscriptionObject = Object.assign({}, subscription, { extra });

编辑:由于您使用的是Push API,因此PushSubscription 中的属性不可枚举。所以subscription 对象的行为与普通对象不太一样,这就是建议的方法不起作用的原因。

但是,您可以先使用PushSubscription.toJSON() 序列化推送订阅,将其序列化为“普通”对象,然后使用建议的技术之一:

subscriptionObject = Object.assign({}, subscription.toJSON(), { extra });

【讨论】:

  • 嗨,谢尔盖,我已经尝试过 Object.assign(),但是订阅对象的内容被额外覆盖,订阅对象的所有属性都丢失了。请参阅我的更新部分。
  • new也尝试了["extra"]的方式,还是当JSON.stringify(newSub)时,多余的部分丢失了。
  • @Franva 我编辑了我的帖子,因为我意识到您正在使用 Push API。试试最新的 sn-p...
  • 这对我有用。是的,事实证明问题来自 PushAPI 返回的订阅。非常感谢您的帮助~!
【解决方案2】:

你为什么不使用简单的属性赋值

let subscription = {..}
const extra = {..}

然后

subscription.extra = extra;

它应该可以工作

【讨论】:

  • 谢谢,这是我尝试过的第一种方法。但没有运气。我 consoloe.log() newSub,我可以在那里看到额外的属性,但问题是当 JSON.stringify(newSub) 时,它丢失了额外的属性。
  • 我刚试了,效果不错,等我打印一下
  • 我刚刚添加了我的控制台截图
  • 谢谢,我确实在我的控制台中尝试过,我可以看到它有效。我添加了我的方法,例如updateSubscription() 和 getSubscription() 没有任何修改。能否请您检查一下,看看是否能发现其中的任何错误?
  • 我找到原因了,请看Sergey.K的回答。原来是 PushAPI 的问题。
【解决方案3】:

这有点hacky,但看起来我们不知道PushSubscription 对象是如何实现的,它可能无法按您预期的那样工作......

...但是它似乎使用自己的方法(根据其 API)正确转换为 JSON,所以您可能想尝试这样的事情:

const newSub = { ...JSON.parse(subscription.toJSON()), extra };

因此,将其转换为 JSON(使用 Push API 中的 toJSON 方法)并返回“普通”javascript 对象,然后将 extra 属性添加到它。

【讨论】:

  • 正确,是因为PushApi实现了自己的toJSON(),所以不得不使用。
  • 我没有为订阅对象测试过它,但通常,尽管它的名字,toJSON 不会返回 JSON。它返回一个可以序列化为 JSON 的值。所以JSON.parse 在这里可能实际上不起作用(它不应该是必需的)。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
【解决方案4】:

你试过了吗

newSub = {
  ...subscription, extra
}

在这种情况下你不需要额外传播。

sub = JSON.stringify(newSub) should result in: "{"endpoint":"dfksjfklsjkld","keys":{"pkey":"dfsfsdfsf","auth":"dfsdfsdfsd"},"extra":{"email":"dfsdfs","ip":"231342.342.342.34"}}"

【讨论】:

  • 感谢Gerrit,请看我的更新部分,我试过你的方法,但是newSub的内容被extra覆盖了,sub的所有属性都丢失了
  • 我添加了一个截图,因为从 js 的角度我不明白为什么有些属性键会被吃掉:)
  • 我找到原因了,请看Sergey.K的回答。原来是 PushAPI 的问题
猜你喜欢
  • 2021-07-06
  • 1970-01-01
  • 1970-01-01
  • 2015-07-24
  • 2023-02-21
  • 2023-02-24
  • 2019-08-08
  • 1970-01-01
  • 2015-07-30
相关资源
最近更新 更多