开发JSON Diff工具:如何获取目标JSON片段在原字符串的索引与长度?
解决JSON Token在原字符串中精准定位的方案
核心问题在于不能依赖JToken序列化后的字符串匹配原JSON(序列化会丢失原缩进、换行等格式),而是要直接通过解析时保留的位置信息来定位目标片段。以下是具体实现步骤:
1. 解析JSON时保留行位置信息
Newtonsoft.Json支持在解析时记录每个Token的行号和列号,通过JsonLoadSettings开启该功能:
var loadSettings = new JsonLoadSettings { LineInfoHandling = LineInfoHandling.Load }; JObject jsonObj = JObject.Parse(originalJsonStr, loadSettings);
2. 获取目标Token的行号与列号
通过SelectToken拿到目标Token后,转换为ILineInfo接口提取位置数据:
JToken targetToken = jsonObj.SelectToken("a[0]"); if (targetToken is not ILineInfo lineInfo) { // 处理未获取到位置信息的异常情况 return; } int startLine = lineInfo.LineNumber; int startColumn = lineInfo.LinePosition;
3. 转换行号列号为原字符串起始索引
遍历原JSON字符串,统计换行符找到目标行,再根据列号定位起始位置:
int startIndex = 0; int currentLine = 1; foreach (char c in originalJsonStr) { if (currentLine == startLine) { // 列号从1开始计数,需减1转为0索引 startIndex += startColumn - 1; break; } if (c == '\n') { currentLine++; } startIndex++; }
4. 计算目标Token的长度
使用JsonTextReader读取原JSON,定位到目标Token后跟踪到其结束位置,计算总长度:
int tokenLength = 0; using (var stringReader = new StringReader(originalJsonStr)) using (var jsonReader = new JsonTextReader(stringReader)) { // 定位到目标Token的起始位置 while (jsonReader.Read()) { if (jsonReader is ILineInfo readerLineInfo && readerLineInfo.LineNumber == startLine && readerLineInfo.LinePosition == startColumn) { int tokenStart = jsonReader.TokenStartIndex; int originalDepth = jsonReader.Depth; JsonToken startTokenType = jsonReader.TokenType; // 读取直到Token结束(对象对应EndObject,数组对应EndArray) while (jsonReader.Read()) { if (jsonReader.Depth == originalDepth && (startTokenType == JsonToken.StartObject && jsonReader.TokenType == JsonToken.EndObject || startTokenType == JsonToken.StartArray && jsonReader.TokenType == JsonToken.EndArray)) { // 结束位置为结束标记的索引+1(包含}或]字符) int tokenEnd = jsonReader.TokenStartIndex + 1; tokenLength = tokenEnd - tokenStart; break; } } break; } } }
5. 在RichTextBox中选中片段
拿到startIndex和tokenLength后,直接调用控件的选中方法:
richTextBox1.SelectionStart = startIndex; richTextBox1.SelectionLength = tokenLength;
注意事项
- 若原JSON使用
\r\n作为换行符,统计行号时需额外判断\r,避免行号计数错误。 - 对于字符串、数字等简单Token,无需跟踪到EndObject/EndArray,直接通过
jsonReader.TokenStartIndex和后续字符位置计算长度即可。
内容的提问来源于stack exchange,提问作者rhobaj
相关产品推荐
相关产品推荐

