Skip to main content
update to show chrome
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139

If you're trying to find a path in a JSON string, you can dump your data into https://jsonpathfinder.com and click on the GUI elements. It'll generate the JS syntax for the patha tool to determine the elementpath for you.

Beyond thatThe Chrome developer console shows property paths when you hover over them in a nested JS object structure, for any arrayswhich can be parsed from JSON using JSON.parse(json) if necessary:

chrome console hovering an object key shows the path to get there on hover

You can also copy the path to the clipboard:

chrome console about to copy property path for object to clipboard using the context menu on a node

Once you mighthave a path on the clipboard, you will typically want to iterate, replace arrays by replacing the relevant array offset indicesindexers like [0] with a loop. You can then use the loop index [i] in its place, or use a forEach style function to replace the whole property and index.

Here's a simpler version ofAnother tool is https://jsonpathfinder.com where you can click on the GUI elements. It'll generate the JS syntax for the path to the element like Chrome.

I wrote a simple tool you can run here, or at https://ggorlen.github.io/json-dive/. ClickPaste your JSON string into the textarea, then click the node you want to copy theits path to your clipboard. It has an option to generate Python/PHP style indexes with brackets rather than dots.

If you're trying to find a path in a JSON string, you can dump your data into https://jsonpathfinder.com and click on the GUI elements. It'll generate the JS syntax for the path to the element.

Beyond that, for any arrays you might want to iterate, replace the relevant array offset indices like [0] with a loop.

Here's a simpler version of the tool you can run here, or at https://ggorlen.github.io/json-dive/. Click the node you want to copy the path to your clipboard.

If you're trying to find a path in a JSON string, you can dump your data into a tool to determine the path for you.

The Chrome developer console shows property paths when you hover over them in a nested JS object structure, which can be parsed from JSON using JSON.parse(json) if necessary:

chrome console hovering an object key shows the path to get there on hover

You can also copy the path to the clipboard:

chrome console about to copy property path for object to clipboard using the context menu on a node

Once you have a path on the clipboard, you will typically want to iterate arrays by replacing the offset indexers like [0] with a loop. You can then use the loop index [i] in its place, or use a forEach style function to replace the whole property and index.

Another tool is https://jsonpathfinder.com where you can click on the GUI elements. It'll generate the JS syntax for the path to the element like Chrome.

I wrote a simple tool you can run here, or at https://ggorlen.github.io/json-dive/. Paste your JSON string into the textarea, then click the node to copy its path to your clipboard. It has an option to generate Python/PHP style indexes with brackets rather than dots.

use textContent instead of innerText for clipboard copy
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
/* code minified to make the tool easier to run without having to scroll */                                                         let bracketsOnly=!1,lastHighlighted={style:{}};const keyToStr=t=>!bracketsOnly&&/^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(t)?`.${toHTML(t)}`:`[&quot;${toHTML(t)}&quot;]`,pathToData=t=>`data-path="data${t.join("")}"`,htmlSpecialChars={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;","\t":"\\t","\r":"\\r","\n":"\\n"," ":"&nbsp;"},toHTML=t=>(""+t).replace(/[&<>"'\t\r\n ]/g,t=>htmlSpecialChars[t]),makeArray=(t,e)=>`\n  [<ul ${pathToData(e)}>\n    ${t.map((t,a)=>{e.push(`[${a}]`);const n=`<li ${pathToData(e)}>\n        ${pathify(t,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>]\n`,makeObj=(t,e)=>`\n  {<ul ${pathToData(e)}>\n    ${Object.entries(t).map(([t,a])=>{e.push(keyToStr(t));const n=`<li ${pathToData(e)}>\n        "${toHTML(t)}": ${pathify(a,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>}\n`,pathify=(t,e=[])=>Array.isArray(t)?makeArray(t,e):"object"==typeof t&&t!=null?makeObj(t,e):toHTML("string"==typeof t?`"${t}"`:t),defaultJSON='{\n  "corge": "test JSON... \\n   asdf\\t asdf",\n  "foo-bar": [\n    {"id": 42},\n    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]\n  ]\n}',$=document.querySelector.bind(document),$$=document.querySelectorAll.bind(document),resultEl=$("#result"),pathEl=$("#path"),tryToJSON=t=>{try{resultEl.innerHTML=pathify(JSON.parse(t)),$("#error").innerText=""}catch(t){resultEl.innerHTML="",$("#error").innerText=t}},copyToClipboard=t=>{const e=document.createElement("textarea");e.innerText=ttextContent=t,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},flashAlert=(t,e=2e3)=>{const a=document.createElement("div");a.textContent=t,a.classList.add("alert"),document.body.appendChild(a),setTimeout(()=>a.remove(),e)},handleClick=t=>{t.stopPropagation(),copyToClipboard(t.target.dataset.path),flashAlert("copied!"),$("#path-out").textContent=t.target.dataset.path},handleMouseOut=t=>{lastHighlighted.style.background="transparent",pathEl.style.display="none"},handleMouseOver=t=>{pathEl.textContent=t.target.dataset.path,pathEl.style.left=`${t.pageX+30}px`,pathEl.style.top=`${t.pageY}px`,pathEl.style.display="block",lastHighlighted.style.background="transparent",(lastHighlighted=t.target.closest("li")).style.background="#0ff"},handleNewJSON=t=>{tryToJSON(t.target.value),[...$$("#result *")].forEach(t=>{t.addEventListener("click",handleClick),t.addEventListener("mouseout",handleMouseOut),t.addEventListener("mouseover",handleMouseOver)})};$("textarea").addEventListener("change",handleNewJSON),$("textarea").addEventListener("keyup",handleNewJSON),$("textarea").value=defaultJSON,$("#brackets").addEventListener("change",t=>{bracketsOnly=!bracketsOnly,handleNewJSON({target:{value:$("textarea").value}})}),handleNewJSON({target:{value:defaultJSON}});
/**/                                                                                       *{box-sizing:border-box;font-family:monospace;margin:0;padding:0}html{height:100%}#path-out{background-color:#0f0;padding:.3em}body{margin:0;height:100%;position:relative;background:#f8f8f8}textarea{width:100%;height:110px;resize:vertical}#opts{background:#e8e8e8;padding:.3em}#opts label{padding:.3em}#path{background:#000;transition:all 50ms;color:#fff;padding:.2em;position:absolute;display:none}#error{margin:.5em;color:red}#result ul{list-style:none}#result li{cursor:pointer;border-left:1em solid transparent}#result li:hover{border-color:#ff0}.alert{background:#f0f;padding:.2em;position:fixed;bottom:10px;right:10px}
<!-- -->                                                                                                    <div class="wrapper"><textarea></textarea><div id="opts"><label>brackets only: <input id="brackets"type="checkbox"></label></div><div id="path-out">click a node to copy path to clipboard</div><div id="path"></div><div id="result"></div><div id="error"></div></div>
let bracketsOnly = false;
let lastHighlighted = {style: {}};

const keyToStr = k =>
  !bracketsOnly && /^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(k) 
    ? `.${toHTML(k)}`
    : `[&quot;${toHTML(k)}&quot;]`
;
const pathToData = p => `data-path="data${p.join("")}"`;

const htmlSpecialChars = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#039;",
  "\t": "\\t",
  "\r": "\\r",
  "\n": "\\n",
  " ": "&nbsp;",
};
const toHTML = x => ("" + x)
  .replace(/[&<>"'\t\r\n ]/g, m => htmlSpecialChars[m])
;

const makeArray = (x, path) => `
  [<ul ${pathToData(path)}>
    ${x.map((e, i) => {
      path.push(`[${i}]`);
      const html = `<li ${pathToData(path)}>
        ${pathify(e, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>]
`;
const makeObj = (x, path) => `
  {<ul ${pathToData(path)}>
    ${Object.entries(x).map(([k, v]) => {
      path.push(keyToStr(k));
      const html = `<li ${pathToData(path)}>
        "${toHTML(k)}": ${pathify(v, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>}
`;

const pathify = (x, path=[]) => {
  if (Array.isArray(x)) {
    return makeArray(x, path);
  }
  else if (typeof x === "object" && x !== null) {
    return makeObj(x, path);
  }
  
  return toHTML(typeof x === "string" ? `"${x}"` : x);
};

const defaultJSON = `{
  "corge": "test JSON... \\n   asdf\\t asdf",
  "foo-bar": [
    {"id": 42},
    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]
  ]
}`;

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
const resultEl = $("#result");
const pathEl = $("#path");

const tryToJSON = v => {
  try {
    resultEl.innerHTML = pathify(JSON.parse(v));
    $("#error").innerText = "";
  }
  catch (err) {
    resultEl.innerHTML = "";
    $("#error").innerText = err;
  }
};

const copyToClipboard = text => {
  const ta = document.createElement("textarea");
  ta.innerTexttextContent = text;
  document.body.appendChild(ta);
  ta.select();
  document.execCommand("copy");
  document.body.removeChild(ta);
};

const flashAlert = (text, timeoutMS=2000) => {
  const alert = document.createElement("div");
  alert.textContent = text;
  alert.classList.add("alert");
  document.body.appendChild(alert);
  setTimeout(() => alert.remove(), timeoutMS);
};

const handleClick = e => {
  e.stopPropagation();
  copyToClipboard(e.target.dataset.path);
  flashAlert("copied!");
  $("#path-out").textContent = e.target.dataset.path;
};

const handleMouseOut = e => {
  lastHighlighted.style.background = "transparent";
  pathEl.style.display = "none";
};

const handleMouseOver = e => {
  pathEl.textContent = e.target.dataset.path;
  pathEl.style.left = `${e.pageX + 30}px`;
  pathEl.style.top = `${e.pageY}px`;
  pathEl.style.display = "block";
  lastHighlighted.style.background = "transparent";
  lastHighlighted = e.target.closest("li");
  lastHighlighted.style.background = "#0ff";
};

const handleNewJSON = e => {
  tryToJSON(e.target.value);
  [...$$("#result *")].forEach(e => {
    e.addEventListener("click", handleClick);
    e.addEventListener("mouseout", handleMouseOut);
    e.addEventListener("mouseover", handleMouseOver);
  });
};
$("textarea").addEventListener("change", handleNewJSON);
$("textarea").addEventListener("keyup", handleNewJSON);
$("textarea").value = defaultJSON;
$("#brackets").addEventListener("change", e => {
  bracketsOnly = !bracketsOnly;
  handleNewJSON({target: {value: $("textarea").value}});
});
handleNewJSON({target: {value: defaultJSON}});
* {
  box-sizing: border-box;
  font-family: monospace;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
}

#path-out {
  background-color: #0f0;
  padding: 0.3em;
}

body {
  margin: 0;
  height: 100%;
  position: relative;
  background: #f8f8f8;
}

textarea {
  width: 100%;
  height: 110px;
  resize: vertical;
}

#opts {
  background: #e8e8e8;
  padding: 0.3em;
}
#opts label {
  padding: 0.3em;
}

#path {
  background: black;
  transition: all 0.05s;
  color: white;
  padding: 0.2em;
  position: absolute;
  display: none;
}

#error {
  margin: 0.5em;
  color: red;
}

#result ul {
  list-style: none;
}

#result li {
  cursor: pointer;
  border-left: 1em solid transparent;
}
#result li:hover {
  border-color: #ff0;
}

.alert {
  background: #f0f;
  padding: 0.2em;
  position: fixed;
  bottom: 10px;
  right: 10px;
}
<div class="wrapper">
  <textarea></textarea>
  <div id="opts">
    <label>
      brackets only: <input id="brackets" type="checkbox">
    </label>
  </div>
  <div id="path-out">click a node to copy path to clipboard</div>
  <div id="path"></div>
  <div id="result"></div>
  <div id="error"></div>
</div>
/* code minified to make the tool easier to run without having to scroll */                                                         let bracketsOnly=!1,lastHighlighted={style:{}};const keyToStr=t=>!bracketsOnly&&/^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(t)?`.${toHTML(t)}`:`[&quot;${toHTML(t)}&quot;]`,pathToData=t=>`data-path="data${t.join("")}"`,htmlSpecialChars={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;","\t":"\\t","\r":"\\r","\n":"\\n"," ":"&nbsp;"},toHTML=t=>(""+t).replace(/[&<>"'\t\r\n ]/g,t=>htmlSpecialChars[t]),makeArray=(t,e)=>`\n  [<ul ${pathToData(e)}>\n    ${t.map((t,a)=>{e.push(`[${a}]`);const n=`<li ${pathToData(e)}>\n        ${pathify(t,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>]\n`,makeObj=(t,e)=>`\n  {<ul ${pathToData(e)}>\n    ${Object.entries(t).map(([t,a])=>{e.push(keyToStr(t));const n=`<li ${pathToData(e)}>\n        "${toHTML(t)}": ${pathify(a,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>}\n`,pathify=(t,e=[])=>Array.isArray(t)?makeArray(t,e):"object"==typeof t&&t!=null?makeObj(t,e):toHTML("string"==typeof t?`"${t}"`:t),defaultJSON='{\n  "corge": "test JSON... \\n   asdf\\t asdf",\n  "foo-bar": [\n    {"id": 42},\n    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]\n  ]\n}',$=document.querySelector.bind(document),$$=document.querySelectorAll.bind(document),resultEl=$("#result"),pathEl=$("#path"),tryToJSON=t=>{try{resultEl.innerHTML=pathify(JSON.parse(t)),$("#error").innerText=""}catch(t){resultEl.innerHTML="",$("#error").innerText=t}},copyToClipboard=t=>{const e=document.createElement("textarea");e.innerText=t,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},flashAlert=(t,e=2e3)=>{const a=document.createElement("div");a.textContent=t,a.classList.add("alert"),document.body.appendChild(a),setTimeout(()=>a.remove(),e)},handleClick=t=>{t.stopPropagation(),copyToClipboard(t.target.dataset.path),flashAlert("copied!"),$("#path-out").textContent=t.target.dataset.path},handleMouseOut=t=>{lastHighlighted.style.background="transparent",pathEl.style.display="none"},handleMouseOver=t=>{pathEl.textContent=t.target.dataset.path,pathEl.style.left=`${t.pageX+30}px`,pathEl.style.top=`${t.pageY}px`,pathEl.style.display="block",lastHighlighted.style.background="transparent",(lastHighlighted=t.target.closest("li")).style.background="#0ff"},handleNewJSON=t=>{tryToJSON(t.target.value),[...$$("#result *")].forEach(t=>{t.addEventListener("click",handleClick),t.addEventListener("mouseout",handleMouseOut),t.addEventListener("mouseover",handleMouseOver)})};$("textarea").addEventListener("change",handleNewJSON),$("textarea").addEventListener("keyup",handleNewJSON),$("textarea").value=defaultJSON,$("#brackets").addEventListener("change",t=>{bracketsOnly=!bracketsOnly,handleNewJSON({target:{value:$("textarea").value}})}),handleNewJSON({target:{value:defaultJSON}});
/**/                                                                                       *{box-sizing:border-box;font-family:monospace;margin:0;padding:0}html{height:100%}#path-out{background-color:#0f0;padding:.3em}body{margin:0;height:100%;position:relative;background:#f8f8f8}textarea{width:100%;height:110px;resize:vertical}#opts{background:#e8e8e8;padding:.3em}#opts label{padding:.3em}#path{background:#000;transition:all 50ms;color:#fff;padding:.2em;position:absolute;display:none}#error{margin:.5em;color:red}#result ul{list-style:none}#result li{cursor:pointer;border-left:1em solid transparent}#result li:hover{border-color:#ff0}.alert{background:#f0f;padding:.2em;position:fixed;bottom:10px;right:10px}
<!-- -->                                                                                                    <div class="wrapper"><textarea></textarea><div id="opts"><label>brackets only: <input id="brackets"type="checkbox"></label></div><div id="path-out">click a node to copy path to clipboard</div><div id="path"></div><div id="result"></div><div id="error"></div></div>
let bracketsOnly = false;
let lastHighlighted = {style: {}};

const keyToStr = k =>
  !bracketsOnly && /^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(k) 
    ? `.${toHTML(k)}`
    : `[&quot;${toHTML(k)}&quot;]`
;
const pathToData = p => `data-path="data${p.join("")}"`;

const htmlSpecialChars = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#039;",
  "\t": "\\t",
  "\r": "\\r",
  "\n": "\\n",
  " ": "&nbsp;",
};
const toHTML = x => ("" + x)
  .replace(/[&<>"'\t\r\n ]/g, m => htmlSpecialChars[m])
;

const makeArray = (x, path) => `
  [<ul ${pathToData(path)}>
    ${x.map((e, i) => {
      path.push(`[${i}]`);
      const html = `<li ${pathToData(path)}>
        ${pathify(e, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>]
`;
const makeObj = (x, path) => `
  {<ul ${pathToData(path)}>
    ${Object.entries(x).map(([k, v]) => {
      path.push(keyToStr(k));
      const html = `<li ${pathToData(path)}>
        "${toHTML(k)}": ${pathify(v, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>}
`;

const pathify = (x, path=[]) => {
  if (Array.isArray(x)) {
    return makeArray(x, path);
  }
  else if (typeof x === "object" && x !== null) {
    return makeObj(x, path);
  }
  
  return toHTML(typeof x === "string" ? `"${x}"` : x);
};

const defaultJSON = `{
  "corge": "test JSON... \\n   asdf\\t asdf",
  "foo-bar": [
    {"id": 42},
    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]
  ]
}`;

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
const resultEl = $("#result");
const pathEl = $("#path");

const tryToJSON = v => {
  try {
    resultEl.innerHTML = pathify(JSON.parse(v));
    $("#error").innerText = "";
  }
  catch (err) {
    resultEl.innerHTML = "";
    $("#error").innerText = err;
  }
};

const copyToClipboard = text => {
  const ta = document.createElement("textarea");
  ta.innerText = text;
  document.body.appendChild(ta);
  ta.select();
  document.execCommand("copy");
  document.body.removeChild(ta);
};

const flashAlert = (text, timeoutMS=2000) => {
  const alert = document.createElement("div");
  alert.textContent = text;
  alert.classList.add("alert");
  document.body.appendChild(alert);
  setTimeout(() => alert.remove(), timeoutMS);
};

const handleClick = e => {
  e.stopPropagation();
  copyToClipboard(e.target.dataset.path);
  flashAlert("copied!");
  $("#path-out").textContent = e.target.dataset.path;
};

const handleMouseOut = e => {
  lastHighlighted.style.background = "transparent";
  pathEl.style.display = "none";
};

const handleMouseOver = e => {
  pathEl.textContent = e.target.dataset.path;
  pathEl.style.left = `${e.pageX + 30}px`;
  pathEl.style.top = `${e.pageY}px`;
  pathEl.style.display = "block";
  lastHighlighted.style.background = "transparent";
  lastHighlighted = e.target.closest("li");
  lastHighlighted.style.background = "#0ff";
};

const handleNewJSON = e => {
  tryToJSON(e.target.value);
  [...$$("#result *")].forEach(e => {
    e.addEventListener("click", handleClick);
    e.addEventListener("mouseout", handleMouseOut);
    e.addEventListener("mouseover", handleMouseOver);
  });
};
$("textarea").addEventListener("change", handleNewJSON);
$("textarea").addEventListener("keyup", handleNewJSON);
$("textarea").value = defaultJSON;
$("#brackets").addEventListener("change", e => {
  bracketsOnly = !bracketsOnly;
  handleNewJSON({target: {value: $("textarea").value}});
});
handleNewJSON({target: {value: defaultJSON}});
* {
  box-sizing: border-box;
  font-family: monospace;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
}

#path-out {
  background-color: #0f0;
  padding: 0.3em;
}

body {
  margin: 0;
  height: 100%;
  position: relative;
  background: #f8f8f8;
}

textarea {
  width: 100%;
  height: 110px;
  resize: vertical;
}

#opts {
  background: #e8e8e8;
  padding: 0.3em;
}
#opts label {
  padding: 0.3em;
}

#path {
  background: black;
  transition: all 0.05s;
  color: white;
  padding: 0.2em;
  position: absolute;
  display: none;
}

#error {
  margin: 0.5em;
  color: red;
}

#result ul {
  list-style: none;
}

#result li {
  cursor: pointer;
  border-left: 1em solid transparent;
}
#result li:hover {
  border-color: #ff0;
}

.alert {
  background: #f0f;
  padding: 0.2em;
  position: fixed;
  bottom: 10px;
  right: 10px;
}
<div class="wrapper">
  <textarea></textarea>
  <div id="opts">
    <label>
      brackets only: <input id="brackets" type="checkbox">
    </label>
  </div>
  <div id="path-out">click a node to copy path to clipboard</div>
  <div id="path"></div>
  <div id="result"></div>
  <div id="error"></div>
</div>
/* code minified to make the tool easier to run without having to scroll */                                                         let bracketsOnly=!1,lastHighlighted={style:{}};const keyToStr=t=>!bracketsOnly&&/^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(t)?`.${toHTML(t)}`:`[&quot;${toHTML(t)}&quot;]`,pathToData=t=>`data-path="data${t.join("")}"`,htmlSpecialChars={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;","\t":"\\t","\r":"\\r","\n":"\\n"," ":"&nbsp;"},toHTML=t=>(""+t).replace(/[&<>"'\t\r\n ]/g,t=>htmlSpecialChars[t]),makeArray=(t,e)=>`\n  [<ul ${pathToData(e)}>\n    ${t.map((t,a)=>{e.push(`[${a}]`);const n=`<li ${pathToData(e)}>\n        ${pathify(t,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>]\n`,makeObj=(t,e)=>`\n  {<ul ${pathToData(e)}>\n    ${Object.entries(t).map(([t,a])=>{e.push(keyToStr(t));const n=`<li ${pathToData(e)}>\n        "${toHTML(t)}": ${pathify(a,e).trim()},\n      </li>`;return e.pop(),n}).join("")}\n  </ul>}\n`,pathify=(t,e=[])=>Array.isArray(t)?makeArray(t,e):"object"==typeof t&&t!=null?makeObj(t,e):toHTML("string"==typeof t?`"${t}"`:t),defaultJSON='{\n  "corge": "test JSON... \\n   asdf\\t asdf",\n  "foo-bar": [\n    {"id": 42},\n    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]\n  ]\n}',$=document.querySelector.bind(document),$$=document.querySelectorAll.bind(document),resultEl=$("#result"),pathEl=$("#path"),tryToJSON=t=>{try{resultEl.innerHTML=pathify(JSON.parse(t)),$("#error").innerText=""}catch(t){resultEl.innerHTML="",$("#error").innerText=t}},copyToClipboard=t=>{const e=document.createElement("textarea");e.textContent=t,document.body.appendChild(e),e.select(),document.execCommand("copy"),document.body.removeChild(e)},flashAlert=(t,e=2e3)=>{const a=document.createElement("div");a.textContent=t,a.classList.add("alert"),document.body.appendChild(a),setTimeout(()=>a.remove(),e)},handleClick=t=>{t.stopPropagation(),copyToClipboard(t.target.dataset.path),flashAlert("copied!"),$("#path-out").textContent=t.target.dataset.path},handleMouseOut=t=>{lastHighlighted.style.background="transparent",pathEl.style.display="none"},handleMouseOver=t=>{pathEl.textContent=t.target.dataset.path,pathEl.style.left=`${t.pageX+30}px`,pathEl.style.top=`${t.pageY}px`,pathEl.style.display="block",lastHighlighted.style.background="transparent",(lastHighlighted=t.target.closest("li")).style.background="#0ff"},handleNewJSON=t=>{tryToJSON(t.target.value),[...$$("#result *")].forEach(t=>{t.addEventListener("click",handleClick),t.addEventListener("mouseout",handleMouseOut),t.addEventListener("mouseover",handleMouseOver)})};$("textarea").addEventListener("change",handleNewJSON),$("textarea").addEventListener("keyup",handleNewJSON),$("textarea").value=defaultJSON,$("#brackets").addEventListener("change",t=>{bracketsOnly=!bracketsOnly,handleNewJSON({target:{value:$("textarea").value}})}),handleNewJSON({target:{value:defaultJSON}});
/**/                                                                                       *{box-sizing:border-box;font-family:monospace;margin:0;padding:0}html{height:100%}#path-out{background-color:#0f0;padding:.3em}body{margin:0;height:100%;position:relative;background:#f8f8f8}textarea{width:100%;height:110px;resize:vertical}#opts{background:#e8e8e8;padding:.3em}#opts label{padding:.3em}#path{background:#000;transition:all 50ms;color:#fff;padding:.2em;position:absolute;display:none}#error{margin:.5em;color:red}#result ul{list-style:none}#result li{cursor:pointer;border-left:1em solid transparent}#result li:hover{border-color:#ff0}.alert{background:#f0f;padding:.2em;position:fixed;bottom:10px;right:10px}
<!-- -->                                                                                                    <div class="wrapper"><textarea></textarea><div id="opts"><label>brackets only: <input id="brackets"type="checkbox"></label></div><div id="path-out">click a node to copy path to clipboard</div><div id="path"></div><div id="result"></div><div id="error"></div></div>
let bracketsOnly = false;
let lastHighlighted = {style: {}};

const keyToStr = k =>
  !bracketsOnly && /^[a-zA-Z_$][a-zA-Z$_\d]*$/.test(k) 
    ? `.${toHTML(k)}`
    : `[&quot;${toHTML(k)}&quot;]`
;
const pathToData = p => `data-path="data${p.join("")}"`;

const htmlSpecialChars = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': "&quot;",
  "'": "&#039;",
  "\t": "\\t",
  "\r": "\\r",
  "\n": "\\n",
  " ": "&nbsp;",
};
const toHTML = x => ("" + x)
  .replace(/[&<>"'\t\r\n ]/g, m => htmlSpecialChars[m])
;

const makeArray = (x, path) => `
  [<ul ${pathToData(path)}>
    ${x.map((e, i) => {
      path.push(`[${i}]`);
      const html = `<li ${pathToData(path)}>
        ${pathify(e, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>]
`;
const makeObj = (x, path) => `
  {<ul ${pathToData(path)}>
    ${Object.entries(x).map(([k, v]) => {
      path.push(keyToStr(k));
      const html = `<li ${pathToData(path)}>
        "${toHTML(k)}": ${pathify(v, path).trim()},
      </li>`;
      path.pop();
      return html;
    }).join("")}
  </ul>}
`;

const pathify = (x, path=[]) => {
  if (Array.isArray(x)) {
    return makeArray(x, path);
  }
  else if (typeof x === "object" && x !== null) {
    return makeObj(x, path);
  }
  
  return toHTML(typeof x === "string" ? `"${x}"` : x);
};

const defaultJSON = `{
  "corge": "test JSON... \\n   asdf\\t asdf",
  "foo-bar": [
    {"id": 42},
    [42, {"foo": {"baz": {"ba  r<>!\\t": true, "4quux": "garply"}}}]
  ]
}`;

const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
const resultEl = $("#result");
const pathEl = $("#path");

const tryToJSON = v => {
  try {
    resultEl.innerHTML = pathify(JSON.parse(v));
    $("#error").innerText = "";
  }
  catch (err) {
    resultEl.innerHTML = "";
    $("#error").innerText = err;
  }
};

const copyToClipboard = text => {
  const ta = document.createElement("textarea");
  ta.textContent = text;
  document.body.appendChild(ta);
  ta.select();
  document.execCommand("copy");
  document.body.removeChild(ta);
};

const flashAlert = (text, timeoutMS=2000) => {
  const alert = document.createElement("div");
  alert.textContent = text;
  alert.classList.add("alert");
  document.body.appendChild(alert);
  setTimeout(() => alert.remove(), timeoutMS);
};

const handleClick = e => {
  e.stopPropagation();
  copyToClipboard(e.target.dataset.path);
  flashAlert("copied!");
  $("#path-out").textContent = e.target.dataset.path;
};

const handleMouseOut = e => {
  lastHighlighted.style.background = "transparent";
  pathEl.style.display = "none";
};

const handleMouseOver = e => {
  pathEl.textContent = e.target.dataset.path;
  pathEl.style.left = `${e.pageX + 30}px`;
  pathEl.style.top = `${e.pageY}px`;
  pathEl.style.display = "block";
  lastHighlighted.style.background = "transparent";
  lastHighlighted = e.target.closest("li");
  lastHighlighted.style.background = "#0ff";
};

const handleNewJSON = e => {
  tryToJSON(e.target.value);
  [...$$("#result *")].forEach(e => {
    e.addEventListener("click", handleClick);
    e.addEventListener("mouseout", handleMouseOut);
    e.addEventListener("mouseover", handleMouseOver);
  });
};
$("textarea").addEventListener("change", handleNewJSON);
$("textarea").addEventListener("keyup", handleNewJSON);
$("textarea").value = defaultJSON;
$("#brackets").addEventListener("change", e => {
  bracketsOnly = !bracketsOnly;
  handleNewJSON({target: {value: $("textarea").value}});
});
handleNewJSON({target: {value: defaultJSON}});
* {
  box-sizing: border-box;
  font-family: monospace;
  margin: 0;
  padding: 0;
}

html {
  height: 100%;
}

#path-out {
  background-color: #0f0;
  padding: 0.3em;
}

body {
  margin: 0;
  height: 100%;
  position: relative;
  background: #f8f8f8;
}

textarea {
  width: 100%;
  height: 110px;
  resize: vertical;
}

#opts {
  background: #e8e8e8;
  padding: 0.3em;
}
#opts label {
  padding: 0.3em;
}

#path {
  background: black;
  transition: all 0.05s;
  color: white;
  padding: 0.2em;
  position: absolute;
  display: none;
}

#error {
  margin: 0.5em;
  color: red;
}

#result ul {
  list-style: none;
}

#result li {
  cursor: pointer;
  border-left: 1em solid transparent;
}
#result li:hover {
  border-color: #ff0;
}

.alert {
  background: #f0f;
  padding: 0.2em;
  position: fixed;
  bottom: 10px;
  right: 10px;
}
<div class="wrapper">
  <textarea></textarea>
  <div id="opts">
    <label>
      brackets only: <input id="brackets" type="checkbox">
    </label>
  </div>
  <div id="path-out">click a node to copy path to clipboard</div>
  <div id="path"></div>
  <div id="result"></div>
  <div id="error"></div>
</div>
add links to GH pages and GH
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139

Here's a simpler version of the tool you can run here, or at https://ggorlen.github.io/json-dive/. Click the node you want to copy the path to your clipboard.

Unminified (also available on GitHub):

Here's a simpler version of the tool you can run here. Click the node you want to copy the path to your clipboard.

Unminified:

Here's a simpler version of the tool you can run here, or at https://ggorlen.github.io/json-dive/. Click the node you want to copy the path to your clipboard.

Unminified (also available on GitHub):

fix null bug
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
make highlighting more efficient
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
make highlighting more efficient
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
improve tab handling
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
improve code
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
improve code
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
add brackets only
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
minify
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading
Source Link
ggorlen
  • 54.2k
  • 7
  • 105
  • 139
Loading