我不建议这样做。问题是,如果您的字符串值属性恰好与您的“空值”"-" 具有相同的值,那么您将在序列化时丢失信息,即该属性是否实际上为空。
话虽如此,您可以通过覆盖字符串值属性的JsonProperty.ValueProvider 来使用ContractResolver 执行此操作:
public class StringRemappingContractResolver : DefaultContractResolver
{
static readonly object NullValue;
static StringRemappingContractResolver() { NullValue = new object(); }
readonly Dictionary<object, object> map;
readonly ILookup<object, object> reverseMap;
public StringRemappingContractResolver() : this(new KeyValuePair<object, object> [] { new KeyValuePair<object, object>(null, "-")})
{
}
public StringRemappingContractResolver(IEnumerable<KeyValuePair<object, object>> map)
{
if (map == null)
throw new ArgumentNullException("map");
this.map = map.ToDictionary(p => p.Key ?? NullValue, p => p.Value);
this.reverseMap = map.ToLookup(p => p.Value ?? NullValue, p => p.Key);
}
class StringRemappingValueProvider : IValueProvider
{
readonly IValueProvider baseProvider;
readonly Dictionary<object, object> map;
readonly ILookup<object, object> reverseMap;
public StringRemappingValueProvider(IValueProvider baseProvider, Dictionary<object, object> map, ILookup<object, object> reverseMap)
{
if (baseProvider == null)
throw new ArgumentNullException("baseProvider");
this.baseProvider = baseProvider;
this.map = map;
this.reverseMap = reverseMap;
}
#region IValueProvider Members
public object GetValue(object target)
{
var value = baseProvider.GetValue(target);
object mapped;
if (map.TryGetValue(value ?? NullValue, out mapped))
value = mapped;
return value;
}
public void SetValue(object target, object value)
{
foreach (var mapped in reverseMap[value ?? NullValue])
{
// Use the first.
value = mapped;
break;
}
baseProvider.SetValue(target, value);
}
#endregion
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.PropertyType == typeof(string))
{
property.ValueProvider = new StringRemappingValueProvider(property.ValueProvider, map, reverseMap);
}
return property;
}
}
然后像这样使用它:
public class User
{
public string user { get; set; }
public string name { get; set; }
public string dni { get; set; }
}
public class TestClass
{
public static void Test()
{
var settings = new JsonSerializerSettings { ContractResolver = new StringRemappingContractResolver() };
var user = new User { user = "usertest", name = "nametest", dni = null };
var json = JsonConvert.SerializeObject(user, settings);
Debug.WriteLine(json); // Prints {"user":"usertest","name":"nametest","dni":"-"}
Debug.Assert(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(@"{'user':'usertest','name':'nametest','dni':'-'}"))); // No assert
var userBack = JsonConvert.DeserializeObject<User>(json, settings);
Debug.Assert(user.dni == userBack.dni && user.name == userBack.name && user.user == userBack.user); // No assert
}
}