[英]Newtonsoft.Json - Deserialize uppercase boolean values without quotes
我从api收到一些不是真正符合ISO标准的Json内容。 布尔值是大写而不是小写。
{ "Bool": False }
最初,我认为应该使用自定义JsonConverter
来解决,如如何使newtonsoft将yes和no反序列化为boolean所示 。
但是看起来好像从未调用过JsonConverter.ReadJson
方法。 我认为原因是值False
不在引号中,因此JsonTextReader
从未调用转换器并创建异常。
处理这种情况的最佳方法是什么?
public class BoolTests
{
public class A
{
[JsonConverter(typeof(CaseIgnoringBooleanConverter))]
public bool Bool { get; set; }
}
[Theory]
[InlineData(false, "{'Bool': false}")] //ok
[InlineData(false, "{'Bool': 'False'}")] // ok
[InlineData(false, "{'Bool': False")] // fails
public void CasingMatters(bool expected, string json)
{
var actual = JsonConvert.DeserializeObject<A>(json);
Assert.Equal(expected, actual.Bool);
}
}
// taken from https://gist.github.com/randyburden/5924981
public class CaseIgnoringBooleanConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.Value.ToString().ToUpperInvariant().Trim())
{
case "TRUE":
return true;
case "FALSE":
return false;
}
// If we reach here, we're pretty much going to throw an error so let's let Json.NET throw it's pretty-fied error message.
return new JsonSerializer().Deserialize(reader, objectType);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(bool);
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
正如拉瑟所说:
无效的json应该在源头修复。
如果您确实需要按原样解析它,则可以将False替换为False(如@Sinatr所建议),如果您希望将其作为字符串,则将其替换为false。
// If you want a string
json.Replace("False", "\"False\"");
// If you want a bool
json.Replace("False", "false");
一个问题是键或另一个值是否包含“ False”模式。
不幸的是,正如您所发现的那样,无效的json是无效的,因此无法由普通和常见的json(反)序列化器(例如Json.net)处理。
为反序列化器使用转换器和策略设置将无法正常工作,因为它们打算处理诸如空对象返回数组或名称转换/大小写处理之类的事情。
一个幼稚的解决方案是做一个简单的字符串替换,例如
string json = invalidJson.Replace("False", "false");
但是,这有一些问题:
False
。 这可能对您的数据不是问题,但使用上述方法并不容易。 一种不同的方法是编写一个基本的令牌解析器,该令牌解析器可以理解基本的JSON语法(例如字符串,数字和标识符),并逐个令牌地遍历文件令牌,从而替换错误的标识符。 这样可以解决问题2,但是根据解决方案的不同,可能需要更复杂的实现来解决内存中的问题1。
以下是创建可使用的TextReader
简单尝试,该方法将在找到标识符时对其进行修复,否则将理解基本的JSON令牌。
请注意以下几点:
List<Test>
,它适用于此。 但是,它的作用是根据您发布的标识符修复无效的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
}
输出(来自LINQPad ):
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.