繁体   English   中英

从文本中提取 JSON

[英]Extract JSON from text

AJAX 调用返回包含 JSON 字符串的响应文本。 我需要:

  1. 提取 JSON 字符串
  2. 修改它
  3. 然后重新插入它以更新原始字符串

我不太担心第 2 步和第 3 步,但我不知道如何执行第 1 步。我正在考虑使用正则表达式,但我不知道如何使用,因为我的 JSON 可能有多个嵌套对象级别或 arrays。

您不能使用正则表达式从任意文本中提取JSON。 由于正则表达式通常不足以验证JSON (除非您可以使用PCRE),因此它们也无法匹配 - 如果可以,它们也可以验证JSON。

但是,如果您知道JSON的顶级元素始终是对象或数组,则可以采用以下方法:

  • 找到字符串中的第一个开头( {[ )和最后一个结束( }] )大括号。
  • 尝试使用JSON.parse()解析该文本块(包括大括号JSON.parse() 如果成功,则完成并返回解析后的结果。
  • 采取前一个结束括号并尝试解析该字符串。 如果成功,你又完成了。
  • 重复此操作,直到你没有支撑或在当前开口支架之前。
  • 在第1步之后找到第一个左大括号。如果没有找到,则该字符串不包含JSON对象/数组,您可以停止。
  • 转到第2步。

这是一个提取JSON对象并返回对象及其位置的函数。 如果你真的需要顶级数组,它应该是扩展:

function extractJSON(str) {
    var firstOpen, firstClose, candidate;
    firstOpen = str.indexOf('{', firstOpen + 1);
    do {
        firstClose = str.lastIndexOf('}');
        console.log('firstOpen: ' + firstOpen, 'firstClose: ' + firstClose);
        if(firstClose <= firstOpen) {
            return null;
        }
        do {
            candidate = str.substring(firstOpen, firstClose + 1);
            console.log('candidate: ' + candidate);
            try {
                var res = JSON.parse(candidate);
                console.log('...found');
                return [res, firstOpen, firstClose + 1];
            }
            catch(e) {
                console.log('...failed');
            }
            firstClose = str.substr(0, firstClose).lastIndexOf('}');
        } while(firstClose > firstOpen);
        firstOpen = str.indexOf('{', firstOpen + 1);
    } while(firstOpen != -1);
}

var obj = {'foo': 'bar', xxx: '} me[ow]'};
var str = 'blah blah { not {json but here is json: ' + JSON.stringify(obj) + ' and here we have stuff that is } really } not ] json }} at all';
var result = extractJSON(str);
console.log('extracted object:', result[0]);
console.log('expected object :', obj);
console.log('did it work     ?', JSON.stringify(result[0]) == JSON.stringify(obj) ? 'yes!' : 'no');
console.log('surrounding str :', str.substr(0, result[1]) + '<JSON>' + str.substr(result[2]));

演示(在nodejs环境中执行,但也应在浏览器中运行): https ://paste.aeum.net/show/81/

对于那些正在寻找(就像我)一般从文本中提取JSON字符串的人(即使它们无效),你可以看看这个Gulp插件https://www.npmjs.com/package/gulp -extract-json-like 它搜索所有看似格式化为JSON字符串的字符串。

创建一个文件夹并安装包。

mkdir project && cd project
npm install gulp gulp-extract-json-like

创建一个./gulpfile.js文件并将以下内容放入其中:

var gulp = require('gulp');
var extractJsonLike = require('gulp-extract-json-like');

gulp.task('default', function () {
  return gulp.src('file.txt')
    .pipe(extractJsonLike())
    .pipe(gulp.dest('dist'));
});

创建一个名为./file.txt的文件,其中包含您的文本并运行以下命令。

gulp

找到的JSON字符串将在./dist/file.txt

如果JSON作为ajax响应的一部分返回,为什么不使用浏览器本机JSON解析(小心陷阱 )? 还是jQuery JSON解析

如果JSON完全被文本破坏了,那真的是一个设计问题恕我直言 - 如果你可以改变它,我强烈建议这样做(即返回一个JSON对象作为响应,文本作为属性物体)。

如果没有,那么使用RegEx将是一场绝对的噩梦。 JSON自然非常灵活,确保准确的解析不仅耗时,而且浪费时间。 我可能会在开始/结束时放入内容标记并希望获得最佳效果。 但是你会对验证错误等持开放态度。

我以自己古怪的方式做到了这一点。 这当然不是万无一失的,但为了提高查看其中包含单行 JSON 对象的日志的能力,这对我有用。 我不是 javascript 开发人员所以请随时告诉我为什么这不好哈哈。

//PrettyPrint() will attempt to find JSON strings in the log message. If it finds them, it will replace the raw ugly JSON with pretty printted JSON
function PrettyPrint() {
    var jsonStrings = [];
    var prettyLogElement = document.getElementById('PrettyLogDisplayOnly');
    try {
        var rawLogMessage = $("textarea[id^='LogMessage']").val();
        if (rawLogMessage == null) {
            throw "Failed to extract original log message.";
        }

        jsonStrings = ExtractJsonStrings(rawLogMessage);
        
        var modifiedLogMessage = "<pre>" + rawLogMessage + "\"</pre>";

        for (const jsonString of jsonStrings) {
            try {
                var jsonObject = JSON.parse(jsonString);
                var prettyPrintJsonString = JSON.stringify(jsonObject, null, 2);
                modifiedLogMessage = modifiedLogMessage.replace(jsonString, prettyPrintJsonString);
            }
            catch (err) {
                modifiedLogMessage += "Failed to pretty print: " + jsonString;
            }
        }
    }
    catch (err) {
        if (err == null || err == undefined) {
            err = "Failed to parse.";
        }
        else
        {
            err = "Failed to parse. Details: " + err;
        }
        
        //TODO: instead of showing the error here, show it as an error banner?
        rawLogMessage = "<br/>Failed to beautify JSON objects. Details: " + err + " Displaying raw log message.<br/>" +
            "<br/>-------------------------------------------------------------------------------------<br/><br/>"
            + rawLogMessage;;

        prettyLogElement.innerHTML += rawLogMessage;
        return;
    }

    prettyLogElement.innerHTML = modifiedLogMessage;
}

function ExtractJsonStrings(rawLogMessage) {
    var jsonStrings = [];
    var locationOfCurrentCurly = -1;
    
    while (true) {
        var countOfOpenCurlyBraces = 0;
        var countOfClosedCurlyBraces = 0;

        var locationOfFirstUnescapedOpeningCurly = GetLocationOfNextUnescapedOpeningCurlyBrace(rawLogMessage, locationOfCurrentCurly + 1);
        if (locationOfFirstUnescapedOpeningCurly == -1) {
            break; //we found all the JSON strings
        }
        else
        {
            locationOfCurrentCurly = locationOfFirstUnescapedOpeningCurly;
            countOfOpenCurlyBraces++;
        }
        
        while (countOfOpenCurlyBraces != countOfClosedCurlyBraces)
        {
            if (countOfClosedCurlyBraces > countOfOpenCurlyBraces)
            {
                throw "Found more closing curly braces than opening curly braces.";
            }
            
            var startSearchAtIndex = locationOfCurrentCurly + 1
            locationOfCurrentCurly = GetLocationOfNextUnescapedCurlyBrace(rawLogMessage, startSearchAtIndex);
            if (locationOfCurrentCurly == -1) {
                throw "Failed to find the 'next' curly brace.";
            }

            var curly = rawLogMessage.charAt(locationOfCurrentCurly);
            if (curly === '{') {
                countOfOpenCurlyBraces++;
            } else if (curly === '}') {
                countOfClosedCurlyBraces++;
            } else {
                throw "Unknown character found when curly brace expected.";
            }
        }
        
        var possiblyCorrectlyFormattedJsonString = rawLogMessage.substring(locationOfFirstUnescapedOpeningCurly, locationOfCurrentCurly + 1);
        jsonStrings.push(possiblyCorrectlyFormattedJsonString);
    }

    return jsonStrings;
}

//this will only find the next opening brace {
function GetLocationOfNextUnescapedOpeningCurlyBrace(rawLogMessage, startIndex) {
    var regexNextUnescapedOpeningCurly = /(?<!\\)({)/i;
    return RegexStringExtract(rawLogMessage, startIndex, regexNextUnescapedOpeningCurly)
}

//this will find the next opening OR closing brace { }
function GetLocationOfNextUnescapedCurlyBrace(rawLogMessage, startIndex) {
    var regexNextUnescapedCurly = /(?<!\\)({|})/i;
    return RegexStringExtract(rawLogMessage, startIndex, regexNextUnescapedCurly)
}

function RegexStringExtract(stringToSearch, startIndex, regex) {
    var substring = stringToSearch.substring(startIndex);
    var regexMatch = regex.exec(substring);
    if (regexMatch) {
        return startIndex + regexMatch.index;
    }
    else {
        return -1;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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