I have some poorly formatted JSON which has newlines in the keys.
The raw JSON looks like:
{
"Some\\nKey": "Some\\nvalue",
"Some nice key": "Some nice value"
}
I have a matching data source which, for keys without newlines works very happily to convert "Some nice key" into "Some nice value". However, when the matching data source contains newlines, the lookup fails. My code looks something like:
const translations = JSON.parse(fileContents);
var value = translations[key];
if (value == null) {
const fixedKey = JSON.stringify(key).replace(/\\n/g, "\\\\n");
value = translations[fixedKey];
if (value == null) {
console.log(`Translation missing for key: ${fixedKey}`);
}
}
The console output is Translation missing for key: Some\\nKey
So, my question is: In Javascript, how do I look up a value of a JSON key with new lines in the key?
Edit:
So, there were two problems with my code - one external to the post, and one internal. The external problem is that the keys coming from the external data source were somehow malformed when doing lookups with newlines - no idea what, but they were. The second issue was with creating fixedKey - JSON.stringify adds " characters to the start and end, so my console output that I originally put was a lie because I wasn't copy/pasting but recreating by hand - I had missed that the output was actually Translation missing for key: "Some\\nKey". The resulting fix was const fixedKey = JSON.stringify(key).replace(/^"/, "").replace(/"$/,""); - using Stringify and stripping the leading and trailing " characters.
This works for me. Not sure what your issue is
const key = "Some Key";
const jsonString = `{ "Some\\nKey": "Some\\nvalue", "Some nice key": "Some nice value" }`
const obj = JSON.parse(jsonString.replaceAll(/\\n/g," "))
console.log(obj[key])
This also works but shows the value with a newline
const key = "Some\nKey";
const jsonString = `{ "Some\\nKey": "Some\\nvalue", "Some nice key": "Some nice value" }`
const obj = JSON.parse(jsonString)
console.log(obj[key])
If your keys and values are the same, it shouldn't matter that there's a \n in the key or not.
const translations = {
"test\n1": 1,
"test\\n2": 2
};
console.log(translations["test\n1"]);
console.log(translations["test\\n2"]);
Related
This is my JS code
add = document.getElementById("add");
add.addEventListener("click", () => {
console.log("Updating List...");
tit = document.getElementById("title").value;
desc = document.getElementById("description").value;
if (localStorage.getItem("itemsJson") == null) {
itemJsonArray = [];
itemJsonArray.push([tit, desc]);
localStorage.setItem("itemsJson", JSON.stringify(itemJsonArray));
}
});
The data should be [ABCD, XYZ]
but it is showing "[[\"\",\"\"]]".
The back slashes are supposed to be there. What you are looking at is a JSON encoded value. They are escaping the double-quotes that are inside the double-quoted string. eg: "This \"string\" value contains quotes".
JavaScript knows how to build the original data structure from that string (using the values shown from the screenshot in your comments)..
var items = localStorage.getItem("itemsJson");
\\ items[0] value is "s"
\\ items[1] value is "d"
I could write the following line of code and get the same result:
var items = "[[\"s\",\"d\"]]";
the JSON string value, eg "[[\"s\",\"d\"]]", starts and ends with quotes and the array that is being serialized contains the string values "s" and "d". If the array contained a number value, eg: 1, as well then the JSON encoded string would look like this:
"[[\"s\",\"d\",1]]"
If you run the following line of code:
alert("This \"string\" value contains quotes");
You would see this message: This "String" value contains quotes and illustrates the same concept of what the backslashes are doing.
I've have structure like below from response
{
"last UpdatedTime":"18:00:01 AM",
"has PErmission":false,
"block UsersSubFeatures":true,
"block CurrentUserTill":"NA",
"unlock UserOnlyForVisibility":"['departmentXYZ']"
}
Note: "unlockUserOnlyForVisibility":"['departmentXYZ']" // array in as string. But want to convert like
{
"last UpdatedTime":"18:00:01 AM",
"has PErmission":false,
"block UsersSubFeatures":true,
"block CurrentUserTill":"NA",
"unlock UserOnlyForVisibility":['departmentXYZ']
}
so i tried JSON.parse(), which gives me Unexpected syntaxError: unexpected token of in JSON at position 1
[Object object ]
so tried regex like below which successfully removes quotes. but not sure this is the proper way to handle this or not
Thanks for the help
As Olyno mentioned, clearly this is something that needs to be fixed backend.
That being said, you need to replace "[with just [ and ]"with ], then handle the single quotes of the elements seperately.
const response = `{
"last UpdatedTime":"18:00:01 AM",
"has PErmission":false,
"block UsersSubFeatures":true,
"block CurrentUserTill":"NA",
"unlock UserOnlyForVisibility":"['hello world', 'test2']"
}`
function cleanBrokenJSONObject(data) {
return data
.replace(/"\[([^\]]*)\]"/g, function(_, singleQuoteElements){
const elements = singleQuoteElements.replace(/'/g, '"');
return `[${elements}]`
})
}
const res = cleanBrokenJSONObject(response);
console.log(JSON.parse(res));
note: this will only work with an array of depth 1 and not more
Break down of the regex: /"\[([^\]]*)\]"/g
"\[ match string that starts with "[
([^\]]*) Capture all the next characters and stop when encountering ] (this is essentially the elements of your array)
\]" match string that ends with ]"
When we capture the elements of the list (2), we replace all the ' with ".
Test cases:
const list1 = `"['hello world', 'test2']"`;
const list2 = `"['','']"`
const list3 = `"['']"`
const list4 = `"[]"`
function cleanBrokenJSONObject(data) {
return data
.replace(/"\[([^\]]*)\]"/g, function(a,b){
const elements = b.replace(/'/g, '"');
return `[${elements}]`
})
}
[list1, list2, list3, list4].forEach(list => {
const res = cleanBrokenJSONObject(list);
console.log(JSON.parse(res));
});
The right way to fix that is to fix your backend, and how you generate and manage the value. If you really want to fix it using your frontend, you need 2 steps:
Convert singles quotes in double quotes
Convert the given string to an object using JSON.parse
So to make these steps, you need this code:
JSON.parse("['departmentXYZ']".replace(/'/g, '"')); // ["departmentXYZ"]
I have a function in Javascript to match text between curly braces (including the braces) in order to extract a JSON string from different posts. The function is below:
function eventObject(value) {
var json = text.match(/{([^}]+)}/)[0]; // matches content between curly braces
return json;
}
The input looks like this:
{
"host": "Host link..",
"info": "Info text...",
"links": [ {"title": "link_title", "url": "link_url"}, {"title": "link_title_2", "url": "link_url_2"} ],
"category": "campfire"
}
The problem is this text contains a nested string with more curly brackets. The output cuts as soon as it gets to the first occurrence of a closing bracket in the links. How can I prevent this from happening in order to get the full string?
Update
I realised I left out some important information to simplify my question: the API response is a string of raw html that contains the string I would like to parse as an object. The typical raw HTML looks like this:
"cooked":"<pre><code class=\"lang-auto\">{\n\"host\": \"host_link\",\n\"info\": \"event_info\",\n\"links\": [{\"title\": \"link_title \", \"url\": \"link_url"},{\"title\": \"link_two_title \", \"url\": \"link_two_url\"} ],\n\"category\": \"category\"\n}\n</code></pre>"}
The challenge is extracting the entire string between <code></code> and parsing it into an object. I have updated the title of the question to reflect this.
The following function successfully extracts the string and strips the html tags and line breaks, but it does not correctly parse it as an object:
function eventObject(value){
const doc = new DOMParser().parseFromString(value, "text/html");
var json = [...doc.querySelectorAll('code')].map(code => code.textContent); // DOMParser extracts text between <code> tags
var final = String(json).replace(/\n/g, " ").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); // removes line breaks and replaces curly quotes with straight quotes
var string = JSON.stringify(final);
var obj = JSON.parse("'" + string + "'");
return obj;
}
Your function eventObject looks ok, but you don't need JSON.stringify because it is intended for serializing JavaScript objects, whereas you pass a string to it. Try this:
function eventObject(value){
const doc = new DOMParser().parseFromString(value, "text/html");
var json = [...doc.querySelectorAll('code')].map(code => code.textContent); // DOMParser extracts text between <code> tags
var final = String(json).replace(/\n/g, " ").replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"'); // removes line breaks and replaces curly quotes with straight quotes
// var string = JSON.stringify(final);
var obj = JSON.parse(final);
return obj;
}
var value = '"cooked":"<pre><code class=\"lang-auto\">{\n\"host\": \"host_link\",\n\"info\": \"event_info\",\n\"links\": [{\"title\": \"link_title \", \"url\": \"link_url"},{\"title\": \"link_two_title \", \"url\": \"link_two_url\"} ],\n\"category\": \"category\"\n}\n</code></pre>"}';
console.log(eventObject(value))
What is the alternative to the following Java code for JavaScript?
String[] strings = {"something written here", "five", "hello world", "this is a really big string", "two words"};
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (String str : strings)
{
bos.write(str.length());
bos.write(str.getBytes());
}
ByteBuffer buffer = ByteBuffer.wrap(bos.toByteArray());
// send buffer somewhere...
I know that I can read Java's ByteBuffer sent over the network with the following JS code, but couldn't figure out a way of writing a reply using JS.
let strings = [];
let bytes = Buffer.from(data);
while (bytes.length > 0) {
let size = bytes.readInt8(0) + 1;
strings.push(bytes.toString("UTF-8", 1, size));
bytes = bytes.slice(size);
}
Let's suppose I want to reply: ["hello", "how", "are you", "doing"]
The closest equivalent to a ByteArrayOutputStream would probably be a Writable stream that is backed by a growing array. I guess you'll have to implement this yourself though, see e.g. in Convert stream into buffer?.
It's probably simpler to directly append buffers to an array as they come in, then concat them all, to implement your growing buffer.
const strings = ["something written here", "five", "hello world", "this is a really big string", "two words"];
const output = [];
for (const str of strings) {
const bytes = Buffer.from(str, "utf-8");
output.push(Buffer.from([bytes.length+1]));
output.push(bytes);
}
const buffer = Buffer.concat(output);
Btw I'm pretty sure you want to use bytes.length, not str.length, for your length-prefixed strings? Same in your Java code, you need to use str.getBytes().length not str.length().
Highest Score Bergi solution is absolutely nice, but I don't get it why he has the code output.push(Buffer.from([bytes.length+1]));, and if I add it, it will go wrong. It will work when I remove it.
I have cookie value stored in following format
{stamp:'HMzWoJn8V4ZkdRN1DduMHLhS3dKiDDr6VoXCjjeuDMO2w6V+n2CcOg==',necessary:true,preferences:true,statistics:true,marketing:false,ver:1}
and i need to read following values of
necessary
preferences
statistics
marketing
Not sure how to to read values correctly, i tried following code assuming it is jSON format
Cookies.get('CookieConsent')
//Parse the cookie to Object
cookieval = Cookies.get('CookieConsent');
console.log(cookieval);
console.log("Necessary: " + Boolean(cookieval.necessary));
console.log("Prefrences: " + Boolean(cookieval.preferences));
console.log("Statistics: " + Boolean(cookieval.statistics));
console.log("Marketing: " + Boolean(cookieval.marketing));
But this code always returns false.
I use following Jquery to read Cookie values https://cdn.jsdelivr.net/npm/js-cookie#2/src/js.cookie.min.js
You do not have JSON format - you have something closer to JS object literal notation, except that it's a string rather than JS code, so can't use JSON.parse unfortunately.
If the values don't have commas or colons, you can split the string by commas and reduce into an object:
const input = `{stamp:'HMzWoJn8V4ZkdRN1DduMHLhS3dKiDDr6VoXCjjeuDMO2w6V+n2CcOg==',necessary:true,preferences:true,statistics:true,marketing:false,ver:1}`;
const obj = input
.slice(1, input.length - 1)
.split(',')
.reduce((obj, str) => {
const [key, val] = str.split(':');
obj[key] = val;
return obj;
}, {});
console.log(obj);
eval is another option, but that's unsafe.
Wrap this string by ( and ). Then parse like as display follow
Attention! But you need be ensure input string (which received from cookie) not contains bad code. Such as unknown injected function. In this case, the function will be executed on client browser, with access to private data (cookie, localStorage, data from html-forms).
const input = "{stamp:'HMzWoJn8V4ZkdRN1DduMHLhS3dKiDDr6VoXCjjeuDMO2w6V+n2CcOg==',necessary:true,preferences:true,statistics:true,marketing:false,ver:1}"
const object = eval("(" + input + ")");
alert(object.necessary);
What about massaging the string into proper JSON, parsing it into a JSON Object, and using the fields from there?
It's less stable in that changes to the input string may break the function, but it is secure in that it's calling JSON.parse() rather than eval().
function reformatCookieInput(inputString) {
inputString = inputString.replace(/'/g, ""); //global strip out single quotes currently wrapping stamp
inputString = inputString.replace(/,/g, `", "`); //global replace commas with wrapped commas
inputString = inputString.replace(/:/g, `":"`); //same idea with colons
inputString = inputString.replace("{", `{"`); //rewrap start of JSON string
inputString = inputString.replace("}", `"}`); //rewrap end of JSON string
return inputString;
}
const input = `{stamp:'HMzWoJn8V4ZkdRN1DduMHLhS3dKiDDr6VoXCjjeuDMO2w6V+n2CcOg==',necessary:true,preferences:true,statistics:true,marketing:false,ver:1}`;
const properJSONObject = JSON.parse(reformatCookieInput(input));
console.log(properJSONObject);