我正在制作网络抓取工具,网页上的大多数数据都是JavaScript对象文字形式的,例如:
// Silly example
var user = {
name: 'John',
surname: 'Doe',
age: 21,
family: [
{
name: 'Jane',
surname: 'Doe',
age: 37
},
// ...
]
};
因此,当我在JavaScript应用中搜索内容时,上面的对象将是:
"{name: 'John', surname: 'Doe', age: 21, family: [{name: 'Jane', surname: 'Doe', age: 37}]}"
是否可以在不使用'eval'或不创建自己的解析器的情况下将其解析为常规JavaScript对象?我看到了与此类似的其他问题,但答案不适用:它们都建议使用JSON.parse()
(不适用)和eval
(出于安全原因,我不能使用它)。例如,In this question的所有答案都暗示eval
或new Function()
基本上是同一件事。
如果没有其他方法,将文字转换为正确的JSON,然后将其解析为JavaScript对象是否可行?
这是我现在尝试过的方法,它可以在一个简单的对象上工作,但是我不确定它在任何地方都可以工作:
const literal = script.innerText.slice(script.innerText.indexOf('{'), script.innerText.lastIndexOf('}') + 1);
const json = literal.replace(/.*:.*(\".*\"|\'.*\'|\[.*\]|\{.*\}|true|false|[0-9]+).*,/g, (prev) => {
let parts = prev.split(':');
let key = '"' + parts.shift().trim() + '"';
let value = parts.join(':').replace(/'.*'/, (a) => {
return '"' + a.slice(1, a.length - 1) + '"';
}).trim();
return key + ':' + value;
});
const obj = JSON.parse(json);
答案 0 :(得分:2)
这是一个简单的演示,您如何使用esprima
获取全局声明的变量
"use strict";
const src = `
var user = {
name: 'John',
surname: 'Doe',
age: 21,
family: [
{
name: 'Jane',
surname: 'Doe',
age: 37
},
// ...
]
};`;
const src2 = `
var a = [1,2,3], b = true;
var s = "some string";
var o = {a:1}, n = null;
var some = {'realy strange' : {"object":"'literal'"}}
`;
function get_globals(src) {
return esprima.parse(src).body
.filter(({type}) => type === "VariableDeclaration") // keep only variables declarations
.map(({declarations}) => declarations)
.flat()
.filter(({type}) => type === "VariableDeclarator")
.reduce((vars, {id: {name}, init}) => {
vars[name] = parse(init);
return vars;
}, {});
}
console.log(get_globals(src));
console.log(get_globals(src2));
/**
* Parse expression
* @param expression
* @returns {*}
*/
function parse(expression) {
switch (expression.type) {
case "ObjectExpression":
return ObjectExpression(expression);
case "Identifier":
return expression.name;
case "Literal":
return expression.value;
case "ArrayExpression":
return ArrayExpression(expression);
}
}
/**
* Parse object expresion
* @param expression
* @returns {object}
*/
function ObjectExpression(expression) {
return expression.properties.reduce((obj, {key, value}) => ({
...obj,
[parse(key)]: parse(value)
}), {});
}
/**
* Parse array expression
* @param expression
* @returns {*[]}
*/
function ArrayExpression(expression) {
return expression.elements.map((exp) => parse(exp));
}
<script src="https://unpkg.com/esprima@~4.0/dist/esprima.js"></script>
答案 1 :(得分:2)
对于这样的数据,您可能可以使用几个正则表达式将其转换为有效的JSON对象。
下面是一个例子。
ps。对于所有对象文字而言,它可能不是100%可靠。
var str = "{name: 'John', surname: 'Doe', age: 21, family: [{name: 'Jane', surname: 'Doe', age: 37}]}";
var jstr = str
.replace(/\'(.*?)\'/g, '"$1"')
.replace(/([\{ ]*?)([a-z]*?)(\:)/gi, '$1"$2"$3');
var obj = JSON.parse(jstr);
console.log(obj);
正如@ ponury-kostek指出的那样,并且我自己使用regEx可能受到限制。 使用诸如Esprima之类的AST解析当然是个好主意,尤其是如果您已经在使用AST解析器。
但是,如果AST解析器过大了,下面使用Javascript的更强大的版本可能会更好。附言同样,它可能不是100%正确,但是它应该可以应付大多数Object文字。
var str = `{
name: 'John:', surname: 'Doe', age: 21,
family: [
{name: '?Jane\\n\\r', surname: 'Doe', age: 37},
{'realy:strange indeed' : {"object":"'\\"literal'"}}
]
}`;
const objLits = [...':0123456789, \t[]{}\r\n'];
function objParse(src) {
const input = [...src];
const output = [];
let inQ = false, inDQ = false,
inEsc = false, inVname = false;
for (const i of input) {
if (inEsc) {
inEsc = false;
output.push(i);
} else if (i === "\\") {
inEsc = true;
output.push(i);
} else if (i === "'" && !inDQ) {
output.push('"');
inQ = !inQ;
} else if (i === '"' && !inQ) {
output.push('"');
inDQ = !inDQ;
} else if (!inVname & !inQ & !inDQ & !inEsc) {
if (objLits.includes(i)) {
output.push(i);
} else {
inVname = true;
output.push('"');
output.push(i);
}
} else if (inVname) {
if (i === ':') {
inVname = false;
output.push('"');
}
output.push(i);
} else {
output.push(i);
}
}
const ostr = output.join('');
return JSON.parse(ostr);
}
console.log(objParse(str));
答案 2 :(得分:1)
假设您使用节点,一个简单的解决方法是
// scraper.js
const fs = require('fs');
const objectString = myScraper.scrape('example.com');
fs.writeFileSync('./scraped.js', objectString);
// myAppUsingTheData.js
const myObj = require('myAppUsingTheData');
但是,需求仍然以某种方式涉及评估。并且您需要单独的过程才能访问您的对象。另外,您还需要以某种方式插入module.exports
。如果只想解析对象,请尝试JSON5
const myObj = JSON5.parse(objectString);
console.log(myObj.name)
使用JSON5将有效地阻止您运行不是应用程序中对象的恶意代码,并且显然可以解析未引用的JSON密钥。
答案 3 :(得分:0)
可以在脚本文本中添加脚本标签:
var JS = `var user = {
name: 'John',
surname: 'Doe',
age: 21,
family: [
{
name: 'Jane',
surname: 'Doe',
age: 37
},
]
};`;
var script = document.createElement('script');
script.textContent = JS
document.head.appendChild(script);
console.log( user )