这就是我所拥有的:
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;
}
}
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="JsonWriter"/> to write to.</param>
/// <param name="value">The value.</param>
/// <param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// can write is set to false
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="JsonReader"/> to read from.</param>
/// <param name="objectType">Type of the object.</param>
/// <param name="existingValue">The existing value of object being read.</param>
/// <param name="serializer">The calling serializer.</param>
/// <returns>The object value.</returns>
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<object> list = new List<object>();
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<string, object> 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.");
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return (objectType == typeof (ExpandoObject));
}
/// <summary>
/// Gets a value indicating whether this <see cref="JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
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<JsonConverter> { new CamelCaseToPascalCaseExpandoObjectConverter() }
};
var json = "{"someProperty":"some value"}";
dynamic deserialized = JsonConvert.DeserializeObject<ExpandoObject>(json, settings);
Console.WriteLine(deserialized.SomeProperty); //some value
var json2 = JsonConvert.SerializeObject(deserialized, Formatting.None, settings);
Console.WriteLine(json == json2); //true
ContractResolver
CamelCasePropertyNamesContractResolver在将对象序列化回JSON并使其再次变为Camel大小写时使用。这也是由JSON.NET提供的。如果你不需要它,你可以省略它。
我忍不住觉得这不是个好主意。您似乎试图保留一种编码约定,但代价是维护有线格式(JSON结构)和逻辑类之间的保真度。这可能会给希望保留JSON类的开发人员带来困惑,如果您还需要或将来需要将这些数据重新序列化为相同的JSON格式,则可能会引发问题。
也就是说,这可能可以通过提前创建.NET类来实现,然后使用DeserializeObject(string value, JsonSerializerSettings settings)
重载,向其传递带有Binder
属性集的JsonSerializerSettings
。您还需要编写一个自定义SerializationBinder
,在其中手动定义JSON类和预定义的.NET类之间的关系。
可以在运行时生成Pascal大小写的类,而无需预定义它们,但我还没有深入研究JSON.NET实现。也许是JsonSerializerSettings
的其他设置之一,比如传递CustomCreationConverter,但我不确定细节。
对于newtonsoft,将此属性添加到您的属性中:
[JsonProperty("schwabFirmId")]
如果你想包含MongoDB,一个更简单的选择(因为你只需要每个类做一次):尝试添加对MongoDB的引用。Son.Serialization.Conventions.
然后将其添加到您的模型构造函数中:
var pack = new ConventionPack { new CamelCaseElementNameConvention(), new IgnoreIfDefaultConvention(true) };
ConventionRegistry.Register("CamelCaseIgnoreDefault", pack, t => true);
任何一个都会保留您最喜欢的C#属性PascalCased和您的json-camelBased。
反序列化会将入站数据视为PascalCased,而序列化会将其更改为camelCase。