System.Text.Json 解析存在于字符串内部的文档
System.Text.Json parse document that exists internal to a string
我收到以 JSON 值(可以是简单的也可以是复杂的)开头的字符串内容,之后还有一些附加内容。我希望能够解析 JSON 文档。
我无法控制该字符串,所以我无法在 JSON 内容后放置任何类型的分隔符来隔离它。
示例:
"true and some more" - yields <true>
"false this is different" - yields <false>
"5.6/7" - yields <5.6>
"\"a string\""; then this" - yields <"a string">
"[null, true]; and some more" - yields <[null, true]>
"{\"key\": \"value\"}, then the end" - yields <{"key": "value"}>
问题是尾随内容。解析器期望输入结束并抛出异常:
')' is invalid after a single JSON value. Expected end of data.
JsonDocumentOptions
中没有允许尾随内容的选项。
作为奖励,如果您能提供使用 ReadOnlySpan<char>
的解决方案,那就太棒了。
自定义 reader 的建议答案对我不起作用,因为基础 reader 中存在问题:它只是不喜欢某些尾随字符。
因为我仍然想依靠 JsonDocument.Parse()
来为我提取元素,所以我真的只需要找到元素停止的位置,将那个位作为一个单独的部分断开,并将其提交给解析方法。这是我想出的:
public static bool TryParseJsonElement(this ReadOnlySpan<char> span, ref int i, out JsonElement element)
{
try
{
int end = i;
char endChar;
switch (span[i])
{
case 'f':
end += 5;
break;
case 't':
case 'n':
end += 4;
break;
case '.': case '-': case '0':
case '1': case '2': case '3':
case '4': case '5': case '6':
case '7': case '8': case '9':
end = i;
var allowDash = false;
while (end < span.Length && (span[end].In('0'..'9') ||
span[end].In('e', '.', '-')))
{
if (!allowDash && span[end] == '-') break;
allowDash = span[end] == 'e';
end++;
}
break;
case '\'':
case '"':
end = i + 1;
endChar = span[i];
while (end < span.Length && span[end] != endChar)
{
if (span[end] == '\')
{
end++;
if (end >= span.Length) break;
}
end++;
}
end++;
break;
case '{':
case '[':
end = i + 1;
endChar = span[i] == '{' ? '}' : ']';
var inString = false;
while (end < span.Length)
{
var escaped = false;
if (span[end] == '\')
{
escaped = true;
end++;
if (end >= span.Length) break;
}
if (!escaped && span[end] == '"')
{
inString = !inString;
}
else if (!inString && span[end] == endChar) break;
end++;
}
end++;
break;
default:
element = default;
return false;
}
var block = span[i..end];
if (block[0] == '\'' && block[^1] == '\'')
block = $"\"{block[1..^1].ToString()}\"".AsSpan();
element = JsonDocument.Parse(block.ToString()).RootElement;
i = end;
return true;
}
catch
{
element = default;
return false;
}
}
它不太关心中间的内容,除了(对于字符串、对象和数组)要知道它是否在字符串的中间(对于找到结束字符有效的位置) ) 并检查 \
分隔的字符。它对我的目的来说效果很好。
它需要一个 ReadOnlySpan<char>
和一个整数作为参考。 i
需要是预期 JSON 值的开始,如果找到有效值,它将前进到之后的下一个字符。它还遵循标准 Try*
模式,即返回带有值输出参数的 bool
。
我收到以 JSON 值(可以是简单的也可以是复杂的)开头的字符串内容,之后还有一些附加内容。我希望能够解析 JSON 文档。
我无法控制该字符串,所以我无法在 JSON 内容后放置任何类型的分隔符来隔离它。
示例:
"true and some more" - yields <true>
"false this is different" - yields <false>
"5.6/7" - yields <5.6>
"\"a string\""; then this" - yields <"a string">
"[null, true]; and some more" - yields <[null, true]>
"{\"key\": \"value\"}, then the end" - yields <{"key": "value"}>
问题是尾随内容。解析器期望输入结束并抛出异常:
')' is invalid after a single JSON value. Expected end of data.
JsonDocumentOptions
中没有允许尾随内容的选项。
作为奖励,如果您能提供使用 ReadOnlySpan<char>
的解决方案,那就太棒了。
自定义 reader 的建议答案对我不起作用,因为基础 reader 中存在问题:它只是不喜欢某些尾随字符。
因为我仍然想依靠 JsonDocument.Parse()
来为我提取元素,所以我真的只需要找到元素停止的位置,将那个位作为一个单独的部分断开,并将其提交给解析方法。这是我想出的:
public static bool TryParseJsonElement(this ReadOnlySpan<char> span, ref int i, out JsonElement element)
{
try
{
int end = i;
char endChar;
switch (span[i])
{
case 'f':
end += 5;
break;
case 't':
case 'n':
end += 4;
break;
case '.': case '-': case '0':
case '1': case '2': case '3':
case '4': case '5': case '6':
case '7': case '8': case '9':
end = i;
var allowDash = false;
while (end < span.Length && (span[end].In('0'..'9') ||
span[end].In('e', '.', '-')))
{
if (!allowDash && span[end] == '-') break;
allowDash = span[end] == 'e';
end++;
}
break;
case '\'':
case '"':
end = i + 1;
endChar = span[i];
while (end < span.Length && span[end] != endChar)
{
if (span[end] == '\')
{
end++;
if (end >= span.Length) break;
}
end++;
}
end++;
break;
case '{':
case '[':
end = i + 1;
endChar = span[i] == '{' ? '}' : ']';
var inString = false;
while (end < span.Length)
{
var escaped = false;
if (span[end] == '\')
{
escaped = true;
end++;
if (end >= span.Length) break;
}
if (!escaped && span[end] == '"')
{
inString = !inString;
}
else if (!inString && span[end] == endChar) break;
end++;
}
end++;
break;
default:
element = default;
return false;
}
var block = span[i..end];
if (block[0] == '\'' && block[^1] == '\'')
block = $"\"{block[1..^1].ToString()}\"".AsSpan();
element = JsonDocument.Parse(block.ToString()).RootElement;
i = end;
return true;
}
catch
{
element = default;
return false;
}
}
它不太关心中间的内容,除了(对于字符串、对象和数组)要知道它是否在字符串的中间(对于找到结束字符有效的位置) ) 并检查 \
分隔的字符。它对我的目的来说效果很好。
它需要一个 ReadOnlySpan<char>
和一个整数作为参考。 i
需要是预期 JSON 值的开始,如果找到有效值,它将前进到之后的下一个字符。它还遵循标准 Try*
模式,即返回带有值输出参数的 bool
。