我想将字符串和可空类型的所有空值序列化为空字符串。我查看了使用自定义JsonConverter
但是带有空值的属性不会传递到转换器中。我研究了使用合约解析器和值提供程序,但它不允许我将int?
的值设置为空字符串。
最有希望的是使用覆盖JsonWriter
方法的自定义WriteNull
,但是当我在转换器的WriteJson
方法中实例化它时,它不会写出任何内容。
public class NullJsonWriter : JsonTextWriter
{
public NullJsonWriter(TextWriter writer) : base(writer)
{
}
public override void WriteNull()
{
base.WriteValue(string.Empty);
}
}
public class GamesJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IEnumerable<IGame>).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer = new NullJsonWriter(new StringWriter(new StringBuilder()));
writer.WriteStartObject();
if (typeof (IEnumerable).IsAssignableFrom(value.GetType()))
{
var list = (IEnumerable<IGame>)value;
if (!list.Any())
{
writer.WriteEndObject();
return;
}
var properties = list.First().GetType().GetProperties();
PropertyInfo gameId = null;
foreach (var prop in properties)
{
if (prop.Name == "GameId")
{
gameId = prop;
break;
}
}
for (int i = 0; i < list.Count(); i++)
{
writer.WritePropertyName(string.Format("{0}", gameId.GetValue(list.ElementAt(i))));
serializer.Serialize(writer, list.ElementAt(i));
}
}
writer.WriteEndObject();
}
我可以在作者的StringBuilder
中看到JSON文本,但它实际上并没有写到我的网页上。
答案 0 :(得分:3)
我不确定我是否理解为什么要写出一个空字符串来代替所有空值,甚至那些可能代表Nullable<int>
或其他对象的空字符串。该策略的问题在于它使得在反序列化期间处理JSON更加困难:如果消费者期望Nullable<int>
并且他们得到字符串,则会导致错误,混乱,并且实质上强制消费代码使用特殊转换器或弱类型对象(例如JObject
,dynamic
)来解决不匹配问题。但是,让我们暂时把它放在一边,说你有理由。
您可以通过继承JsonTextWriter
类并重写WriteNull
方法来编写空字符串来完成您想要的操作。诀窍是你必须实例化自己的JsonSerializer
而不是JsonConvert.SerializeObject
,因为后者没有任何接受JsonWriter
的重载。这是一个简短的概念验证,表明这个想法将起作用:
class Program
{
static void Main(string[] args)
{
// Set up a dummy object with some data. The object also has a bunch
// of properties that are never set, so they have null values by default.
Foo foo = new Foo
{
String = "test",
Int = 123,
Decimal = 3.14M,
Object = new Bar { Name = "obj1" },
Array = new Bar[]
{
new Bar { Name = "obj2" }
}
};
// The serializer writes to the custom NullJsonWriter, which
// writes to the StringWriter, which writes to the StringBuilder.
// We could also use a StreamWriter in place of the StringWriter
// if we wanted to write to a file or web response or some other stream.
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
using (NullJsonWriter njw = new NullJsonWriter(sw))
{
JsonSerializer ser = new JsonSerializer();
ser.Formatting = Formatting.Indented;
ser.Serialize(njw, foo);
}
// Get the JSON result from the StringBuilder and write it to the console.
Console.WriteLine(sb.ToString());
}
}
class Foo
{
public string String { get; set; }
public string NullString { get; set; }
public int? Int { get; set; }
public int? NullInt { get; set; }
public decimal? Decimal { get; set; }
public decimal? NullDecimal { get; set; }
public Bar Object { get; set; }
public Bar NullObject { get; set; }
public Bar[] Array { get; set; }
public Bar[] NullArray { get; set; }
}
class Bar
{
public string Name { get; set; }
}
public class NullJsonWriter : JsonTextWriter
{
public NullJsonWriter(TextWriter writer) : base(writer)
{
}
public override void WriteNull()
{
base.WriteValue(string.Empty);
}
}
这是输出:
{
"String": "test",
"NullString": "",
"Int": 123,
"NullInt": "",
"Decimal": 3.14,
"NullDecimal": "",
"Object": {
"Name": "obj1"
},
"NullObject": "",
"Array": [
{
"Name": "obj2"
}
],
"NullArray": ""
}
小提琴:https://dotnetfiddle.net/QpfG8D
现在让我们来谈谈为什么这种方法在转换器中没有按预期工作。当JSON.Net调用您的转换器时,它已经实例化JsonWriter
,它将被传递到writer
方法的WriteJson
参数。您没有写入该实例,而是使用新的NullJsonWriter
实例覆盖此变量。但请记住,替换引用变量的内容不会替换变量引用的原始实例。您已成功写入转换器中的新编写器实例,但您正在编写的所有输出都将进入StringBuilder
方法开头实例化的WriteJson
。由于您从未从StringBuilder
获取JSON结果并对其执行某些操作,因此在方法结束时会将其丢弃。与此同时,Json.Net继续使用其原始JsonWriter
实例,您没有在转换器中写入任何内容。所以,这就是为什么你没有在最终输出中看到自定义的JSON。
有几种方法可以修复您的代码。一种方法是在转换器中实例化NullJsonWriter
而不是劫持writer
变量时使用新变量。然后,在WriteJson
方法的末尾,从StringBuilder
获取JSON,并使用原始编写器上的WriteRawValue
方法将其写入原始编写器。
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
StringBuilder sb = new StringBuilder();
using (var innerWriter = new NullJsonWriter(new StringWriter(sb))
{
innerWriter.WriteStartObject();
// ... (snip) ...
innerWriter.WriteEndObject();
}
writer.WriteRawValue(sb.ToString());
}
另一种方法,取决于您的预期结果,根本不是在转换器中使用单独的编写器,而是在序列化的最开始创建NullJsonWriter
实例并将其传递给外部序列化器(正如我在帖子前面的概念验证中所做的那样)。当Json.Net调用你的转换器时,传递给WriteJson
的作者将是你的自定义NullJsonWriter
,所以此时你可以自然地写它而不必跳过箍。但请记住,使用此方法,整个 JSON将使用空字符串替换空值,而不仅仅是转换器处理的IEnumerable<IGame>
。如果您试图将特殊行为限制在转换器中,那么这种方法对您不起作用。这实际上取决于你想要实现的目标 - 你想要将null替换到任何地方或只是部分JSON吗?我从你的问题中没有很好地理解这一点。
无论如何,希望这有帮助。
答案 1 :(得分:0)
JsonSerializer _jsonWriter = new JsonSerializer
{
NullValueHandling = NullValueHandling.Include
};
使用此命令使序列化程序不会忽略空值,那么您应该能够使用已有的代码覆盖序列化:)