使用Json.NET的Pascal案例动态属性

这就是我所拥有的:

using Newtonsoft.Json; var json = "{\"someProperty\":\"some value\"}"; dynamic deserialized = JsonConvert.DeserializeObject(json); 

这很好用:

 Assert.That(deserialized.someProperty.ToString(), Is.EqualTo("some value")); 

我希望这可以工作(属性大写的第一个字母)而不改变json

 Assert.That(deserialized.SomeProperty.ToString(), Is.EqualTo("some value")); 

我同意Avner Shahar-Kashtan的看法。 你不应该这样做 ,特别是如果你无法控制JSON。

也就是说,可以使用ExpandoObject和自定义ExpandoObjectConverter来完成 。 JSON.NET已经提供了一个ExpandoObjectConverter,所以通过一些小的调整就可以得到你想要的东西。

请注意代码段中的// CHANGED注释,以显示我更改它的位置。

 public class CamelCaseToPascalCaseExpandoObjectConverter : JsonConverter { //CHANGED //the ExpandoObjectConverter needs this internal method so we have to copy it //from JsonReader.cs internal static bool IsPrimitiveToken(JsonToken token) { switch (token) { case JsonToken.Integer: case JsonToken.Float: case JsonToken.String: case JsonToken.Boolean: case JsonToken.Null: case JsonToken.Undefined: case JsonToken.Date: case JsonToken.Bytes: return true; default: return false; } } ///  /// Writes the JSON representation of the object. ///  /// The  to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // can write is set to false } ///  /// Reads the JSON representation of the object. ///  /// The  to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. /// The object value. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return ReadValue(reader); } private object ReadValue(JsonReader reader) { while (reader.TokenType == JsonToken.Comment) { if (!reader.Read()) throw new Exception("Unexpected end."); } switch (reader.TokenType) { case JsonToken.StartObject: return ReadObject(reader); case JsonToken.StartArray: return ReadList(reader); default: //CHANGED //call to static method declared inside this class if (IsPrimitiveToken(reader.TokenType)) return reader.Value; //CHANGED //Use string.format instead of some util function declared inside JSON.NET throw new Exception(string.Format(CultureInfo.InvariantCulture, "Unexpected token when converting ExpandoObject: {0}", reader.TokenType)); } } private object ReadList(JsonReader reader) { IList list = new List(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.Comment: break; default: object v = ReadValue(reader); list.Add(v); break; case JsonToken.EndArray: return list; } } throw new Exception("Unexpected end."); } private object ReadObject(JsonReader reader) { IDictionary expandoObject = new ExpandoObject(); while (reader.Read()) { switch (reader.TokenType) { case JsonToken.PropertyName: //CHANGED //added call to ToPascalCase extension method string propertyName = reader.Value.ToString().ToPascalCase(); if (!reader.Read()) throw new Exception("Unexpected end."); object v = ReadValue(reader); expandoObject[propertyName] = v; break; case JsonToken.Comment: break; case JsonToken.EndObject: return expandoObject; } } throw new Exception("Unexpected end."); } ///  /// Determines whether this instance can convert the specified object type. ///  /// Type of the object. ///  /// true if this instance can convert the specified object type; otherwise, false. ///  public override bool CanConvert(Type objectType) { return (objectType == typeof (ExpandoObject)); } ///  /// Gets a value indicating whether this  can write JSON. ///  ///  /// true if this  can write JSON; otherwise, false. ///  public override bool CanWrite { get { return false; } } } 

Pascal Case转换器的简单字符串。 如果需要,让它变得更聪明。

 public static class StringExtensions { public static string ToPascalCase(this string s) { if (string.IsNullOrEmpty(s) || !char.IsLower(s[0])) return s; string str = char.ToUpper(s[0], CultureInfo.InvariantCulture).ToString((IFormatProvider)CultureInfo.InvariantCulture); if (s.Length > 1) str = str + s.Substring(1); return str; } } 

现在你可以像这样使用它。

 var settings = new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver(), Converters = new List { new CamelCaseToPascalCaseExpandoObjectConverter() } }; var json = "{\"someProperty\":\"some value\"}"; dynamic deserialized = JsonConvert.DeserializeObject(json, settings); Console.WriteLine(deserialized.SomeProperty); //some value var json2 = JsonConvert.SerializeObject(deserialized, Formatting.None, settings); Console.WriteLine(json == json2); //true 

在将对象序列化回JSON并再次使其成为Camel大小写时,将使用ContractResolver CamelCasePropertyNamesContractResolver 。 这也是由JSON.NET提供的。 如果您不需要,可以省略它。

我不禁觉得这不是一个好主意。 看起来您正在尝试保留编码约定,但代价是保持有线格式(JSON结构)和逻辑类之间的保真度。 对于希望保留JSON类的开发人员而言,这可能会造成混淆,如果您还需要或将来需要将此数据重新序列化为相同的JSON格式,则可能会导致问题。

也就是说,这可能是通过提前创建.NET类,然后使用DeserializeObject(string value, JsonSerializerSettings settings)重载, JsonSerializerSettings传递带有Binder属性集的JsonSerializerSettings来实现的。 您还需要编写一个自定义SerializationBinder ,您可以在其中手动定义JSON类与预定义.NET类之间的关系。

有可能在运行时生成Pascal类,而不预先定义它们,但我还没有深入研究JSON.NET实现。 也许其他JsonSerializerSettings设置之一,如传递CustomCreationConverter,但我不确定细节。

你必须将你的JSON更改为, {\"SomeProperty\":\"some value\"}

对于newtonsoft,将此属性添加到您的属性:

 [JsonProperty("schwabFirmId")] 

如果你想要包含MongoDB,那么一个更简单的选项(因为你只需要每个类做一次):尝试添加对MongoDB.Bson.Serialization.Conventions的引用。

然后在模型构造函数中添加:

 var pack = new ConventionPack { new CamelCaseElementNameConvention(), new IgnoreIfDefaultConvention(true) }; ConventionRegistry.Register("CamelCaseIgnoreDefault", pack, t => true); 

任何一个都会保留你最喜欢的C#属性PascalCased和你的json camelCased。

反序列化将入站数据视为PascalCased,序列化将其更改为camelCase。