2 回答
TA贡献1828条经验 获得超6个赞
不幸的是,正如您所发现的,无效的 json 是无效的,因此无法由普通和常见的 json (de) 序列化程序(如 Json.net)处理。
对反序列化程序使用转换器和策略设置也不起作用,因为它们旨在处理诸如空对象作为数组返回或名称转换/大小写处理之类的事情。
一个天真的解决方案是做一个简单的字符串替换,比如
string json = invalidJson.Replace("False", "false");
但是,这存在一些问题:
您需要将整个无效的 json 读入内存,并创建它的固定副本,这意味着您将在内存中拥有两个完整的数据副本,一个是坏的,一个是好的。
它也会替换内部字符串。这可能不是您的数据问题,但使用上述方法并不容易处理。
False
另一种方法是编写一个基本的分词器,该分词器可以理解基本的 JSON 语法,例如字符串、数字和标识符,并逐个令牌遍历文件,替换错误的标识符。这将解决问题 2,但根据解决方案,可能需要更复杂的实现来修复内存问题 1。
下面发布了一个简单的尝试,创建一个可以使用的标识符,这将在找到标识符时修复它们,否则可以理解基本的 JSON 令牌。TextReader
请注意以下几点:
它不是真正的性能。它始终分配临时缓冲区。您可能希望研究“缓冲区租用”以更好地处理此方法,甚至只是直接流式传输到缓冲区。
它不处理数字,因为那时我停止编写代码。我把这个留作练习。可以编写基本的数字处理,因为您并没有真正验证文件是否具有有效的 JSON,因此可以添加任何可以获取足够字符来构成数字的内容。
我没有用非常大的文件对此进行测试,只用小的示例文件进行测试。我复制了一个9.5MB的文本,它适用于此。
List<Test>
我没有测试所有 JSON 语法。可能存在应该处理但未处理的字符。如果您最终使用它,请创建大量测试!
但是,它的作用是根据您发布的标识符修复无效的 JSON,并且它以流式方式执行此操作。因此,无论您的 JSON 文件有多大,这都应该可用。
无论如何,这是代码,再次注意有关数字的异常:
void Main()
{
using (var file = File.OpenText(@"d:\temp\test.json"))
using (var fix = new MyFalseFixingTextReader(file))
{
var reader = new JsonTextReader(fix);
var serializer = new JsonSerializer();
serializer.Deserialize<Test>(reader).Dump();
}
}
public class MyFalseFixingTextReader : TextReader
{
private readonly TextReader _Reader;
private readonly StringBuilder _Buffer = new StringBuilder(32768);
public MyFalseFixingTextReader(TextReader reader) => _Reader = reader;
public override void Close()
{
_Reader.Close();
base.Close();
}
public override int Read(char[] buffer, int index, int count)
{
TryFillBuffer(count);
int amountToCopy = Math.Min(_Buffer.Length, count);
_Buffer.CopyTo(0, buffer, index, amountToCopy);
_Buffer.Remove(0, amountToCopy);
return amountToCopy;
}
private (bool more, char c) TryReadChar()
{
int i = _Reader.Read();
if (i < 0)
return (false, default);
return (true, (char)i);
}
private (bool more, char c) TryPeekChar()
{
int i = _Reader.Peek();
if (i < 0)
return (false, default);
return (true, (char)i);
}
private void TryFillBuffer(int count)
{
if (_Buffer.Length >= count)
return;
while (_Buffer.Length < count)
{
var (more, c) = TryPeekChar();
if (!more)
break;
switch (c)
{
case '{':
case '}':
case '[':
case ']':
case '\r':
case '\n':
case ' ':
case '\t':
case ':':
case ',':
_Reader.Read();
_Buffer.Append(c);
break;
case '"':
_Buffer.Append(GrabString());
break;
case char letter when char.IsLetter(letter):
var identifier = GrabIdentifier();
_Buffer.Append(ReplaceFaultyIdentifiers(identifier));
break;
case char startOfNumber when startOfNumber == '-' || (startOfNumber >= '0' && startOfNumber <= '9'):
_Buffer.Append(GrabNumber());
break;
default:
throw new InvalidOperationException($"Unable to cope with character '{c}' (0x{((int)c).ToString("x2")})");
}
}
}
private string ReplaceFaultyIdentifiers(string identifier)
{
switch (identifier)
{
case "False":
return "false";
case "True":
return "true";
case "Null":
return "null";
default:
return identifier;
}
}
private string GrabNumber()
{
throw new NotImplementedException("Left as an excercise");
// See https://www.json.org/ for the syntax
}
private string GrabIdentifier()
{
var result = new StringBuilder();
while (true)
{
int i = _Reader.Peek();
if (i < 0)
break;
char c = (char)i;
if (char.IsLetter(c))
{
_Reader.Read();
result.Append(c);
}
else
break;
}
return result.ToString();
}
private string GrabString()
{
_Reader.Read();
var result = new StringBuilder();
result.Append('"');
while (true)
{
var (more, c) = TryReadChar();
if (!more)
return result.ToString();
switch (c)
{
case '"':
result.Append(c);
return result.ToString();
case '\\':
result.Append(c);
(more, c) = TryReadChar();
if (!more)
return result.ToString();
switch (c)
{
case 'u':
result.Append(c);
for (int index = 1; index <= 4; index++)
{
(more, c) = TryReadChar();
if (!more)
return result.ToString();
result.Append(c);
}
break;
default:
result.Append(c);
break;
}
break;
default:
result.Append(c);
break;
}
}
}
}
public class Test
{
public bool False1 { get; set; }
public bool False2 { get; set; }
public bool False3 { get; set; }
}
示例文件:
{
"false1": false,
"false2": "false",
"false3": False
}
输出:
TA贡献1843条经验 获得超7个赞
应在源中修复无效的 json。
如果你真的需要按原样解析它,如果你想把它作为一个字符串,你可以用“False”替换 False,如果你想把它作为一个布尔值,你可以用 false 替换它。
// If you want a string
json.Replace("False", "\"False\"");
// If you want a bool
json.Replace("False", "false");
一个问题是,如果一个键或其他值包含“False”模式。
- 2 回答
- 0 关注
- 172 浏览
添加回答
举报