C# 如何使用Json.Net将所有空字符串序列化为空字符串
我想将字符串和可空类型中的所有空字符串序列化为空字符串。我研究了如何使用自定义的C# 如何使用Json.Net将所有空字符串序列化为空字符串,c#,json,serialization,json.net,C#,Json,Serialization,Json.net,我想将字符串和可空类型中的所有空字符串序列化为空字符串。我研究了如何使用自定义的JsonConverter,但是带有null的属性不会传递到转换器中。我考虑过使用契约解析器和值提供程序,但它不允许我将int?的值设置为空字符串。 最有希望的是使用一个定制的JsonWriter来覆盖WriteNull方法,但是当我在转换器的WriteJson方法中实例化它时,它不会写出任何内容 public class NullJsonWriter : JsonTextWriter { public Nu
JsonConverter
,但是带有null的属性不会传递到转换器中。我考虑过使用契约解析器和值提供程序,但它不允许我将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();
}
公共类NullJsonWriter:JsonTextWriter
{
公共NullJsonWriter(TextWriter编写器):基(编写器)
{
}
公共重写void WriteNull()
{
base.WriteValue(string.Empty);
}
}
公共类游戏JsonConverter:JsonConverter
{
公共覆盖布尔CanConvert(类型objectType)
{
返回类型(IEnumerable).IsAssignableFrom(objectType);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
抛出新的NotImplementedException();
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
writer=newnulljsonwriter(newstringwriter(newstringbuilder()));
writer.WriteStartObject();
if(typeof(IEnumerable).IsAssignableFrom(value.GetType()))
{
变量列表=(IEnumerable)值;
如果(!list.Any())
{
writer.WriteEndObject();
回来
}
var properties=list.First().GetType().GetProperties();
PropertyInfo gameId=null;
foreach(属性中的var属性)
{
如果(道具名称=“玩家ID”)
{
配子体=道具;
打破
}
}
对于(int i=0;i
我可以在作者的
StringBuilder
中看到JSON文本,但它实际上并没有写入我的网页。我不确定我是否理解为什么要写出一个空字符串来代替所有空字符串,甚至那些可能代表可空的或其他对象的字符串。这种策略的问题是这使得JSON在反序列化过程中更难处理:如果使用者期望一个可空的
,而他们得到一个字符串,则会导致错误和混乱,并从本质上迫使使用者代码使用特殊的转换器或弱类型对象(例如作业对象
,动态
)为了避免这种不匹配,我们暂时把它放在一边,说你有你的理由
通过对JsonTextWriter
类进行子类化并重写WriteNull
方法来编写空字符串,您完全可以随心所欲。诀窍是您必须实例化自己的JsonSerializer
而不是使用JsonConvert.serialized对象,因为后者没有任何overloads接受一个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": ""
}
小提琴:
现在,让我们讨论一下为什么这种方法在转换器中没有像预期的那样工作。当JSON.Net调用转换器时,它已经实例化了一个JsonWriter
,它被传递到WriteJson
方法的writer
参数中。您所做的不是写入该实例,而是覆盖t使用新的NullJsonWriter
实例输入此变量。但请记住,替换引用变量的内容不会替换该变量引用的原始实例。您正在转换器中成功写入新的writer实例,但您正在写入的所有输出都将进入StringBuilder您在WriteJson
方法开始时实例化的code>。由于您从未从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
的writer将是您的自定义NullJsonWriter
,因此在这一点上,您可以自然地对其进行写入,而无需跳转。但是,请记住,使用这种方法,整个Json将用空字符串代替null,而不是just转换器处理的IEnumerable
。如果您试图将特殊行为仅限于转换器,则此方法不适用于您
JsonSerializer _jsonWriter = new JsonSerializer
{
NullValueHandling = NullValueHandling.Include
};