我正在使用Azure使用数据库优先的EF方法。Azure Web服务中我的一个实体之一定义如下:
public class Company : EntityData
{
public string CompanyName { get; set; }
}
它从EntityData继承ID属性。ID属性是类型字符串。
在客户端,我有以下实体:
class Company
{
[JsonConverter(typeof(IntConverter))]
public int Id { get; set; }
public string CompanyName { get; set; }
}
如上所述,我需要将ID从字符串转换为int。
我已经创建了以下JSON转换器:
class IntConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
return 0;
int num;
if (Int32.TryParse(reader.Value.ToString(), out num))
return num;
else
return 0;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
serializer.Serialize(writer, value.ToString());
}
}
它工作正常,但是由于这是我的第一个JSON转换器,因此我不确定是否正确创建了它。我看到了转换器的示例,他们使用了现有价值,而不是读取器。就我而言,现有值始终为0。
上述实施是否正确?
您的代码基本上是正确的。
existingValue
是先前存在于父c#模型中的值。通过将其传递到转换器中,JSON.NET允许转换器在填充现有模型或仅读取集合时工作。例如,如果您具有带有转换器的预先读取的属性:
public class RootObject
{
readonly ObservableCollection<SomeClass> _collection = new ObservableCollection<SomeClass>();
[JsonConverter(typeof(ObservableCollectionConverter<SomeClass>>)]
public ObservableCollection<SomeClass> { get { return _collection; } }
}
然后,ObservableCollectionConverter<SomeClass>
的ReadJson()
方法将通过预先分配的_collection
值并能够将项目添加到它。
话虽如此,有一些改进要做:
在
WriteJson()
中,您致电value.ToString()
。ToString()
有可能返回具有文化特定的字符串,例如可能会插入NumberGroupSeparator
的小数点。相反,您应该以不变格式序列化如下:public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // Int32 implements the IConvertible interface which has a ToString() overload // that takes an IFormatProvider specification. Pass the invariant format to guarantee // identical serialization in all cultures. var convertible = (IConvertible)value; serializer.Serialize(writer, convertible.ToString(NumberFormatInfo.InvariantInfo)); }
ReadJson()
中需要进行类似的修复:int num; if (Int32.TryParse(reader.Value.ToString(), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out num)) return num; else return 0;
您的
ReadJson()
与意外数据没有弹性。例如,如果传递的JSON实际上具有数组或对象值(例如{"unexpectedProperty": "unexpectedValue"}
),则JsonReader
将无法正确地提出到输入末尾。您应该检查reader.TokenType
并适当处理不良数据,例如:public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { switch (reader.TokenType) { case JsonToken.Null: return null; case JsonToken.Integer: // Input was already an integer. Return it return (int)JToken.Load(reader); case JsonToken.String: { int num; if (Int32.TryParse(reader.Value.ToString(), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out num)) return num; else return 0; } default: throw new JsonSerializationException(string.Format("Unexpected token {0} at path {1}", reader.TokenType, reader.Path)); }
或,如果您不愿意消耗并丢弃意外数据而不抛出异常(我不建议这样做),则可以使用
JsonReader.Skip()
:default: Debug.WriteLine(string.Format("Unexpected token {0} at path {1}", reader.TokenType, reader.Path)); reader.Skip(); return 0;
当通过属性应用转换器时,JsonConverter.CanConvert
未调用。但是,如果将转换器添加到JsonSerializerSettings.Converters
中,那么从CanConvert
返回true
将是一个问题。相反,我建议抛出NotImplementedException
或正确实现该方法:public override bool CanConvert(Type objectType) { return objectType == typeof(int) || objectType == typeof(int?); }
因此,您的最终转换器可能看起来像:
class IntConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(int) || objectType == typeof(int?);
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Null:
return null;
case JsonToken.Integer:
// Input was already an integer. Return it
return (int)JToken.Load(reader);
case JsonToken.String:
{
int num;
if (Int32.TryParse(reader.Value.ToString(), NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out num))
return num;
else
return 0;
}
default:
throw new JsonSerializationException(string.Format("Unexpected token {0} at path {1}", reader.TokenType, reader.Path));
}
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
// Int32 implements the IConvertible interface which has a ToString() overload
// that takes an IFormatProvider specification. Pass the invariant format to guarantee
// identical serialization in all cultures.
var convertible = (IConvertible)value;
serializer.Serialize(writer, convertible.ToString(NumberFormatInfo.InvariantInfo));
}
}
样品小提琴显示了最终转换器的严格和耐受版本。