将JSON字符串定位到对象中的路径

Max*_*xim 15 javascript json

我有一个类似于这个的JSON字符串:

{
    "Version": "XXX",
    "Statements": [
        {...},
        {...},
        {...}
    ]
}
Run Code Online (Sandbox Code Playgroud)

如何找出XX在JSON字符串的字符中定义Statements属性中的哪个对象?(考虑到那些对象可以任意深度嵌套).

例如,如果我有一个字符串

{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}]}
--------------------------------------------------------
123456789 123456789 123456789 123456789 123456789 123456
Run Code Online (Sandbox Code Playgroud)

然后位置上的字符36将对应于第一个语句对象,而位置上的字符52将对应于第三个语句对象.

str*_*tss 6

这是一些不需要外部库的脏解决方案:

const data = '{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}],"some":0}';

const getValuesPositionInArray = arrayKey => data => {
  const arrayNameSeparator = `"${arrayKey}":`;
  const targetArrayIndexOf = data.indexOf(arrayNameSeparator) + arrayNameSeparator.length;
  const arrayStringWithRest = data.slice(targetArrayIndexOf, data.length);
  
  const { result } = arrayStringWithRest.split('').reduce(
    (acc, char, idx, array) => {
      if (acc.finished) return acc;
      if (!acc.processingKey && char === '[') acc.nesting += 1;
      if (!acc.processingKey && char === ']') acc.nesting -= 1;
      
      const shouldFinish = acc.nesting === 0;
      const charIsDblQuote = char === '"';
      const charBefore = array[idx - 1];
      const charAfter = array[idx + 1];
      
      acc.position += 1;
      acc.finished = shouldFinish;

      if (acc.processingKey && !charIsDblQuote) acc.processedKey += char;
      if (charIsDblQuote) acc.processingKey = !acc.processingKey;
      if (charIsDblQuote && !acc.processingKey && charAfter === ':') {
      	acc.result[acc.processedKey] = acc.position;
        acc.processedKey = '';
      }
      
      return acc;
    }, 
    { 
      finished: false, 
      processingKey: false,
      processedKey: '',
      nesting: 0,
      position: targetArrayIndexOf + 1,
      result: {}
    }
  )
  
  return result;
  
}

const result = getValuesPositionInArray('Statements')(data);

console.log(result)
Run Code Online (Sandbox Code Playgroud)

但是,如果目标对象包含字符串值,则此片段将中断.

编辑

以下是更新的片段,其中包含字符串值修复和解析值:

const data = '{"Version":"XXX","Statements":[{"aa":"some"},{"b":"ano:,{ther}"},{"bb":3}],"some":0}';

const getValuesPositionInArray = arrayKey => data => {
  const arrayNameSeparator = `"${arrayKey}":`;
  const targetArrayIndexOf = data.indexOf(arrayNameSeparator) + arrayNameSeparator.length;
  const arrayStringWithRest = data.slice(targetArrayIndexOf, data.length);
  const charsAfterValue = ['}', ','];
  const charsBeforeKey = ['{', ','];

  const { result } = arrayStringWithRest.split('').reduce(
    (acc, char, idx, array) => {
      if (acc.finished) return acc;
      if (!acc.processingKey && !acc.processingValue && char === '[') acc.nesting += 1;
      if (!acc.processingKey && !acc.processingValue && char === ']') acc.nesting -= 1;

      const shouldFinish = acc.nesting === 0;
      const charIsDblQuote = char === '"';
      const charBefore = array[idx - 1];
      const charAfter = array[idx + 1];
     
      const keyProcessingStarted = (
        charIsDblQuote &&
        !acc.processingKey &&
        !acc.processingValue &&
        charsBeforeKey.includes(charBefore)
      );

      const keyProcessingFinished = (
        charAfter === ':' &&
        charIsDblQuote && 
        acc.processingKey 
      );

      const valueProcessingStarted = (
        char === ':' &&
        !acc.processingKey &&
        !acc.processingValue
      );

      const valueProcessingFinished = (
        (acc.lastProcessedValueType === String
          ? charIsDblQuote
          : true
        ) &&
        acc.processingValue &&
        charsAfterValue.includes(charAfter)
      );

      acc.position += 1;
      acc.finished = shouldFinish;

      if (acc.processingKey && !charIsDblQuote) acc.processedKey += char;
      if (acc.processingValue && !charIsDblQuote) acc.processedValue += char;
      
      if (keyProcessingStarted) {
        acc.processingKey = true;
      } else if (keyProcessingFinished) {
        acc.processingKey = false;
        acc.result[acc.processedKey] = { position: acc.position };
        acc.lastProcessedKey = acc.processedKey;
        acc.processedKey = '';
      }

      if (valueProcessingStarted) {
        acc.processingValue = true;
        acc.lastProcessedValueType = charAfter === '"' ? String : Number;
      } else if (valueProcessingFinished) {
        acc.processingValue = false;
      	acc.result[acc.lastProcessedKey].value = (
          acc.lastProcessedValueType(acc.processedValue)
        );
        acc.processedValue = '';
        acc.lastProcessedKey = '';
        acc.lastProcessedValueType = (v) => v;
      }

      return acc;
    },
    {
      finished: false,
      processingKey: false,
      processingValue: false,
      processedKey: '',
      processedValue: '',
      lastProcessedKey: '',
      lastProcessedValueType: (v) => v,
      nesting: 0,
      position: targetArrayIndexOf + 1,
      result: {}
    }
  )

  return result;

}

const result = getValuesPositionInArray('Statements')(data);

console.log(result)
Run Code Online (Sandbox Code Playgroud)


Eme*_*eus 5

要在json字符串中找到某些内容的位置,如果要构建自己的算法,需要考虑几个因素,一个问题是几个字符串可能导致相同的对象字面值,也就是属性的顺序.对象不保证,那么相同的字符串可能会导致属性中的顺序不同.我们知道字符串中的所有.方法{,但[可能意味着[{.因此,为了找到1例如的位置,我们应该删除原始字符串中的空格并执行递归循环并再次构建json并找到匹配项.这里只是一个找到1的位置的例子:

var json = '{"Version":"XXX","Statements":[{"a":1},{"b":2},{"b":3}]}';

var obj = JSON.parse(json)

var str2 = ""

for(p in obj){
    str2 += "{";
    str2 += p+":";
    if(p == "Statements"){
        str2 += ":["
        obj[p].forEach(o=>{            
            for(p2 in o){
                if(p2 == "a"){
                    str2 += '{"a":'
                }
            }
        })
    }else{
        str2 +='"'+obj[p]+'",'
    }    
}
console.log(str2)
console.log(str2.length+1)
Run Code Online (Sandbox Code Playgroud)

这个例子不准确,它只是向您展示一种可能的方法.在真实和通用的解决方案中,您应该考虑数百种事情.


Max*_*xim 5

在做了大量研究之后,我认为我没有使用esprima包编写自己的解析器.因为esprima不是特定于JSON(而是JavaScript),所以我必须将我的JSON字符串包装到括号中.

树中的每个元素都包含一个loc属性,其范围与原始JSON字符串中的位置相匹配.

var esprima = require("esprima");
var JSONPath = require('JSONPath');

function getStatementIndex(str, line, column) {
	var tree = esprima.parseScript(str, {loc:true});
	var query = "$.body[0].expression.properties[?(@.key.value=='Statement')].value.elements[*].loc";
	var locations = JSONPath({json: tree, path: query});
	
	console.log(locations);
	
	for(var i = 0; i < locations.length; i++) {
		var loc = locations[i];
		
		var contains = false;
		
		if (loc.start.line < line && loc.end.line > line) {
			continue;
		}
		
		// If a single line and in between
		if (loc.start.line == loc.end.line && loc.start.line == line) {
			if (loc.start.column <= column && loc.end.column >= column) {
				contains = true;
			}
			
		// If on the beginning line
		} else if (loc.start.line == line && loc.start.column <= column) {
			contains = true;
		
		// If on the end line
		} else if (loc.end.line == line && loc.end.column >= column) {
			contains = true;
		
		// If in between
		} else if (loc.start.line < line  && loc.end.line > line) {
			contains = true;
		}
					
		if (contains)
			return i;
	}
	
	return -1;
}

var result = getStatementIndex(str, 81, 7);
Run Code Online (Sandbox Code Playgroud)