JSON.stringify() потеря вложенных свойств Я знаю причину, но не знаю ответа

#javascript #json #stringify

#javascript #json #stringify

Вопрос:

У меня есть 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, все свойства подписки будут потеряны.

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. Попробовал еще 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));
  

введите описание изображения здесь

Комментарии:

1. Почему дополнительные свойства не поддаются перечислению? Что это за специальный объект?

2. что такое subscriptionObject ?

3. где subscriptionObject установлено?

4. Ах, действительно хороший улов

5. Может быть, явно скопируйте нужные вам свойства из этого объекта? Поскольку это хост-объект, вы не можете делать никаких предположений о том, как он реализован (я думаю). Согласно спецификации, он фактически реализует свою собственную процедуру сериализации JSON. Так что, может быть, вы хотите сделать sub = subscription.toJSON(); sub.extra = extra; JSON.stringify(sub) вместо этого.

Ответ №1:

Если изменение subscription в порядке, вы можете просто использовать:

 subscription['extra'] = extra;
  

Если вам нужен новый объект, вы можете использовать:

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

РЕДАКТИРОВАТЬ: Поскольку вы работаете с Push API, свойства в PushSubscription не поддаются перечислению. Таким образом, subscription объект ведет себя не совсем как обычный объект, поэтому предлагаемые подходы не работают.

Однако вы можете сериализовать push-подписку с помощью PushSubscription.toJSON() , сначала сериализуя ее в «обычный» объект, а затем используя один из предложенных методов:

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

Комментарии:

1. привет, Сергей, я попробовал Object.assign(), но содержимое subscriptionObject перезаписано extra, все свойства объекта подписки потеряны. Пожалуйста, смотрите мой раздел обновления.

2. new также попробовал подход [«extra»], но при JSON.stringify(newSub) дополнительная часть теряется.

3. @Franva Я отредактировал свой пост теперь, когда понял, что вы работаете с Push API. Попробуйте последний фрагмент…

4. это работает для меня. Да, оказывается, проблема связана с подпиской, возвращаемой из PushAPI. Огромное спасибо за вашу помощь ~!

Ответ №2:

почему бы вам не использовать подобное простое присвоение свойства

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

затем

 subscription.extra = extra;
  

это должно сработать

введите описание изображения здесь

Комментарии:

1. спасибо, чувак, это мой первый подход, который я попробовал. но не повезло. Я consoloe.log() newSub, я вижу там дополнительное свойство, но проблема в том, что когда JSON.stringify(newSub), он потерял дополнительное свойство.

2. я только что попробовал, и это сработало, подождите, дайте мне распечатать вас

3. я только что добавил скриншот своей консоли

4. спасибо, чувак, я попробовал это в своей консоли, и я вижу, что это работает. Я добавил свои методы, например, updateSubscription() и getSubscription() как есть без каких-либо изменений. Не могли бы вы, пожалуйста, просмотреть их и посмотреть, сможете ли вы обнаружить там какие-либо ошибки?

5. Я выяснил причину, пожалуйста, смотрите SERGEY. Ответ К. Оказывается, это проблема из PushAPI.

Ответ №3:

Это немного халтурно, но, похоже, мы не знаем, как реализован PushSubscription объект, и он может работать не так, как вы ожидаете…

… однако, похоже, он правильно преобразуется в JSON, используя свой собственный метод (в соответствии с его API), поэтому вы можете попробовать что-то вроде этого:

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

Таким образом, преобразуем его в JSON (используя toJSON метод в Push API) и обратно в «обычный» объект javascript, а затем добавляем к нему extra свойство.

Комментарии:

1. правильно, это потому, что PushApi реализовал свой собственный toJSON(), поэтому я должен его использовать.

2. Я не тестировал его для объекта подписки, но обычно, несмотря на его название, 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"}}"
  

введите описание изображения здесь

Комментарии:

1. спасибо, Геррит, пожалуйста, посмотрите Мой раздел обновления, я попробовал ваш способ, но содержимое newSub перезаписано extra, все свойства sub потеряны

2. Я добавил скриншот, потому что я не понимаю с точки зрения js, почему некоторые ключи свойств съедаются 🙂

3. Я выяснил причину, пожалуйста, смотрите SERGEY. Ответ К. Оказывается, это проблема из PushAPI