toxic53 toxic53 - 3 months ago 8
Javascript Question

JSON.parse with special chracters

I'm sorry for posting this but I after so many hours of pulling my hair I can't seem to make any more progress.

I have a JSON string that is being returned to me as part of a JSON object like:

{ "name": "value", "artifact": "THE_JSON_STRING" }


I'm pulling the artifact out and using JSON.parse works fine most of the time but I came across an issue when the artifact string contains a /" and JSON.parse breaks. I've gone over many solutions and tried what seems like everything but there has to be something I'm doing wrong. For simplicity this is my code:

var str = '{ "name" : "This is some value \"escaped\""}';
str = str.replace(/\\"/g, 'x')
var obj = JSON.parse(str);


Any help would be much appreciated

https://fiddle.jshell.net/u428x1k7/2/

Edit

I'm very new to Javascript but I'm using React with Redux to make a service call to get my data. Then I try to use the data via props:

Raw Data Coming from Service Call:

{
"id": "8b6df083-8f4f-440a-bc67-4c57bf0969bf",
"name": "My API ",
"description": "All APIs are added to this category if one is not specified",
"icon": "fa-university",
"services": [
{
"id": "16e32cfd-b6e7-4c49-b5ef-ebb5456e6639",
"artifact": "{ \"swagger\": \"2.0\", \"info\": { \"version\": \"1.0.0\", \"title\": \"Swagger Petstore\", \"license\": { \"name\": \"MIT\" } }, \"host\": \"petstore.swagger.io\", \"basePath\": \"/v1\", \"schemes\": [ \"http\" ], \"consumes\": [ \"application/json\" ], \"produces\": [ \"application/json\" ], \"paths\": { \"/pets\": { \"get\": { \"summary\": \"List all pets\", \"operationId\": \"listPets\", \"tags\": [ \"pets\" ], \"parameters\": [ { \"name\": \"limit\", \"in\": \"query\", \"description\": \"How many items to return at one time (max 100)\", \"required\": false, \"type\": \"integer\", \"format\": \"int32\" } ], \"responses\": { \"200\": { \"description\": \"An paged array of pets\", \"headers\": { \"x-next\": { \"type\": \"string\", \"description\": \"A link to the next page of responses\" } }, \"schema\": { \"$ref\": \"#/definitions/Pets\" } }, \"default\": { \"description\": \"unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } }, \"post\": { \"summary\": \"Create a pet\", \"operationId\": \"createPets\", \"tags\": [ \"pets\" ], \"responses\": { \"201\": { \"description\": \"Null response\" }, \"default\": { \"description\": \"unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } }, \"/pets/{petId}\": { \"get\": { \"summary\": \"Info for a specific pet\", \"operationId\": \"showPetById\", \"tags\": [ \"pets\" ], \"parameters\": [ { \"name\": \"petId\", \"in\": \"path\", \"required\": true, \"description\": \"The id of the pet to retrieve\", \"type\": \"string\" } ], \"responses\": { \"200\": { \"description\": \"Expected response to a valid request\", \"schema\": { \"$ref\": \"#/definitions/Pets\" } }, \"default\": { \"description\": \"unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } } }, \"definitions\": { \"Pet\": { \"required\": [ \"id\", \"name\" ], \"properties\": { \"id\": { \"type\": \"integer\", \"format\": \"int64\" }, \"name\": { \"type\": \"string\" }, \"tag\": { \"type\": \"string\" } } }, \"Pets\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Pet\" } }, \"Error\": { \"required\": [ \"code\", \"message\" ], \"properties\": { \"code\": { \"type\": \"integer\", \"format\": \"int32\" }, \"message\": { \"type\": \"string\" } } } } }"
},
{
"id": "b4328bbc-bcb0-4313-b80c-a4394e553dd9",
"artifact": "{ \"swagger\": \"2.0\", \"info\": { \"title\": \"Uber API\", \"description\": \"Move your app forward with the Uber API\", \"version\": \"1.0.0\" }, \"host\": \"api.uber.com\", \"schemes\": [ \"https\" ], \"basePath\": \"/v1\", \"produces\": [ \"application/json\" ], \"paths\": { \"/products\": { \"get\": { \"summary\": \"Product Types\", \"description\": \"The Products endpoint returns information about the Uber products offered at a given location. The response includes the display name and other details about each product, and lists the products in the proper display order.\", \"parameters\": [ { \"name\": \"latitude\", \"in\": \"query\", \"description\": \"Latitude component of location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"longitude\", \"in\": \"query\", \"description\": \"Longitude component of location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" } ], \"tags\": [ \"Products\" ], \"responses\": { \"200\": { \"description\": \"An array of products\", \"schema\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Product\" } } }, \"default\": { \"description\": \"Unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } }, \"/estimates/price\": { \"get\": { \"summary\": \"Price Estimates\", \"description\": \"The Price Estimates endpoint returns an estimated price range for each product offered at a given location. The price estimate is provided as a formatted string with the full price range and the localized currency symbol.<br><br>The response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for situations requiring currency conversion. When surge is active for a particular product, its surge_multiplier will be greater than 1, but the price estimate already factors in this multiplier.\", \"parameters\": [ { \"name\": \"start_latitude\", \"in\": \"query\", \"description\": \"Latitude component of start location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"start_longitude\", \"in\": \"query\", \"description\": \"Longitude component of start location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"end_latitude\", \"in\": \"query\", \"description\": \"Latitude component of end location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"end_longitude\", \"in\": \"query\", \"description\": \"Longitude component of end location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" } ], \"tags\": [ \"Estimates\" ], \"responses\": { \"200\": { \"description\": \"An array of price estimates by product\", \"schema\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/PriceEstimate\" } } }, \"default\": { \"description\": \"Unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } }, \"/estimates/time\": { \"get\": { \"summary\": \"Time Estimates\", \"description\": \"The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs.\", \"parameters\": [ { \"name\": \"start_latitude\", \"in\": \"query\", \"description\": \"Latitude component of start location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"start_longitude\", \"in\": \"query\", \"description\": \"Longitude component of start location.\", \"required\": true, \"type\": \"number\", \"format\": \"double\" }, { \"name\": \"customer_uuid\", \"in\": \"query\", \"type\": \"string\", \"format\": \"uuid\", \"description\": \"Unique customer identifier to be used for experience customization.\" }, { \"name\": \"product_id\", \"in\": \"query\", \"type\": \"string\", \"description\": \"Unique identifier representing a specific product for a given latitude & longitude.\" } ], \"tags\": [ \"Estimates\" ], \"responses\": { \"200\": { \"description\": \"An array of products\", \"schema\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Product\" } } }, \"default\": { \"description\": \"Unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } }, \"/me\": { \"get\": { \"summary\": \"User Profile\", \"description\": \"The User Profile endpoint returns information about the Uber user that has authorized with the application.\", \"tags\": [ \"User\" ], \"responses\": { \"200\": { \"description\": \"Profile information for a user\", \"schema\": { \"$ref\": \"#/definitions/Profile\" } }, \"default\": { \"description\": \"Unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } }, \"/history\": { \"get\": { \"summary\": \"User Activity\", \"description\": \"The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.<br><br>The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary.\", \"parameters\": [ { \"name\": \"offset\", \"in\": \"query\", \"type\": \"integer\", \"format\": \"int32\", \"description\": \"Offset the list of returned results by this amount. Default is zero.\" }, { \"name\": \"limit\", \"in\": \"query\", \"type\": \"integer\", \"format\": \"int32\", \"description\": \"Number of items to retrieve. Default is 5, maximum is 100.\" } ], \"tags\": [ \"User\" ], \"responses\": { \"200\": { \"description\": \"History information for the given user\", \"schema\": { \"$ref\": \"#/definitions/Activities\" } }, \"default\": { \"description\": \"Unexpected error\", \"schema\": { \"$ref\": \"#/definitions/Error\" } } } } } }, \"definitions\": { \"Product\": { \"properties\": { \"product_id\": { \"type\": \"string\", \"description\": \"Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles.\" }, \"description\": { \"type\": \"string\", \"description\": \"Description of product.\" }, \"display_name\": { \"type\": \"string\", \"description\": \"Display name of product.\" }, \"capacity\": { \"type\": \"string\", \"description\": \"Capacity of product. For example, 4 people.\" }, \"image\": { \"type\": \"string\", \"description\": \"Image URL representing the product.\" } } }, \"PriceEstimate\": { \"properties\": { \"product_id\": { \"type\": \"string\", \"description\": \"Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles\" }, \"currency_code\": { \"type\": \"string\", \"description\": \"[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code.\" }, \"display_name\": { \"type\": \"string\", \"description\": \"Display name of product.\" }, \"estimate\": { \"type\": \"string\", \"description\": \"Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or \"Metered\" for TAXI.\" }, \"low_estimate\": { \"type\": \"number\", \"description\": \"Lower bound of the estimated price.\" }, \"high_estimate\": { \"type\": \"number\", \"description\": \"Upper bound of the estimated price.\" }, \"surge_multiplier\": { \"type\": \"number\", \"description\": \"Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier.\" } } }, \"Profile\": { \"properties\": { \"first_name\": { \"type\": \"string\", \"description\": \"First name of the Uber user.\" }, \"last_name\": { \"type\": \"string\", \"description\": \"Last name of the Uber user.\" }, \"email\": { \"type\": \"string\", \"description\": \"Email address of the Uber user\" }, \"picture\": { \"type\": \"string\", \"description\": \"Image URL of the Uber user.\" }, \"promo_code\": { \"type\": \"string\", \"description\": \"Promo code of the Uber user.\" } } }, \"Activity\": { \"properties\": { \"uuid\": { \"type\": \"string\", \"description\": \"Unique identifier for the activity\" } } }, \"Activities\": { \"properties\": { \"offset\": { \"type\": \"integer\", \"format\": \"int32\", \"description\": \"Position in pagination.\" }, \"limit\": { \"type\": \"integer\", \"format\": \"int32\", \"description\": \"Number of items to retrieve (100 max).\" }, \"count\": { \"type\": \"integer\", \"format\": \"int32\", \"description\": \"Total number of items available.\" }, \"history\": { \"type\": \"array\", \"items\": { \"$ref\": \"#/definitions/Activity\" } } } }, \"Error\": { \"properties\": { \"code\": { \"type\": \"integer\", \"format\": \"int32\" }, \"message\": { \"type\": \"string\" }, \"fields\": { \"type\": \"string\" } } } } }"
}
]
}


Action:

export const RECEIVE_CATEGORY = 'RECEIVE_CATEGORY';
function receiveCategory(category) {
return {
type: RECEIVE_CATEGORY,
category
}
}

export function fetchCategory(categoryID) {
return function(dispatch) {
dispatch(requestCategory(categoryID))

return fetch(`${baseURL}/categories/${categoryID}`, {
method: 'GET',
headers: {
'Accept' : 'application/json'
}
})
.then(response => response.json())
.then(json => dispatch(receiveCategory(json)))
.catch(function(error) {
console.log(error);
});
}
}


Reducer:
function categories(state = [], action) {

switch(action.type) {
case RECEIVE_CATEGORIES :
return action.categories;
case RECEIVE_CATEGORY :

/* TODO : there has to be an easier way ...*/
var index = 0;
for(index; index < state.length; index++) {
if(action.category.id === state[index].id) {
break;
}
}

var array= [...state.slice(0, index),
action.category,
...state.slice(index+1)
];
return array;
default:
return state;
}
}

Answer

You have to double escape the backslash when representing the JSON as a string. When you actually fetch that data from a service or API, you can just parse it and it should work fine. Check out the example below.

var obj = {
  "name": "This is some value \"escaped\"",
  "artifact": "THE_JSON_STRING"
};

var test = JSON.stringify(obj);
console.log(test);

var str = '{"name":"This is some value \\"escaped\\"","artifact":"THE_JSON_STRING"}';
console.log(test);
console.log(test === str);

try {
  console.log(JSON.parse(str).name);
} catch (error) {
  console.log(error);
}

Comments