简体   繁体   中英

JSON.stringify() losing nested properties I know the cause but don't know the answer

I have 2 objects:

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

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

I would like to put the extra object inside subscription, so it looks like:

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

then I need to send it as body of a http request:

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), 
});

but I found no matter what I do, I always lose the extra property inside subscription in the process of JSON.stringify() .

I know the cause: it's because that the properties in extra object are not enumerable.

So far, I have tried:

1.use the spread:

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

but the content of newSub will be exactly same with extra, the properties of subscription are all lost.

2.add toJSON function into the place where I generate the extra object

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;
      }
    };    
}

no effect at all.

I attach my code here:

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;
  }
}

Update

1.Tried 1 more approach:

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

here is the output screenshot:

在此处输入图片说明

2.Also this one:

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

And here is the screenshot of output:

在此处输入图片说明

3.with string index approach:

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

在此处输入图片说明

If mutating subscription is OK, you can just use:

subscription['extra'] = extra;

If you want a new object, you can use:

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

EDIT: Since you are working with the Push API , the properties in PushSubscription are not enumerable. So the subscription object does not behave quite like a normal object, which is why the suggested approaches have not been working.

However, you can serialize the push subscription using PushSubscription.toJSON() first to serialize it to a "normal" object, then use one of the suggested techniques:

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

Have you tried

newSub = {
  ...subscription, extra
}

you don't need to spread extra in this case.

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

在此处输入图片说明

why don't you use like simple assignment of property

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

then

subscription.extra = extra;

it should work

在此处输入图片说明

This is a bit hacky, but it looks like we don't know how the PushSubscription object is implemented, and it may not work as you expect...

... however it seems to convert to JSON properly using its own method (according to its API), so you may want to try something like this:

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

Thus, converting it to JSON (using the toJSON method in the Push API) and back to a "normal" javascript object, -then- adding the extra property to it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM