2012-02-12 6 views
14

とのダイナミックな性質はこれは私が持っているものです。パスカルケースJson.NET

using Newtonsoft.Json; 

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

これは正常に動作します:

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

私はこれがなくて(大文字にプロパティの最初の文字を)仕事をしたいですjsonを変更する:あなたがあなたのJSONを変更する必要が

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

答えて

0

{\"SomeProperty\":\"some value\"}

+1

ありがとうございます!私はJSONを制御することはできないので、不可能です。 – dillenmeister

2

これは良い考えではありません。コーディング規則を維持しようとしているようですが、ワイヤ形式(JSON構造体)とロジッククラスの間の忠実性を維持することを犠牲にしています。これは、JSONクラスが保持されることを期待する開発者にとって混乱を招く可能性があります。また、このデータを同じJSON形式に再直列化する必要がある場合や今後必要になる場合は、問題を引き起こす可能性があります。

これは、あらかじめ.NETクラスを作成してから、DeserializeObject(string value, JsonSerializerSettings settings)オーバーロードを使用して、Binderプロパティを設定してJsonSerializerSettingsに渡すことで実現できます。また、JSONクラスと事前定義された.NETクラスの間の関係を手動で定義するカスタムSerializationBinderを記述する必要があります。

パスカル・ケース・クラスを事前定義せずに実行時に生成することは可能かもしれませんが、JSON.NETの実装にはそれほど深く掘り下げていません。おそらく、他のJsonSerializerSettingsの設定の1つ、CustomCreationConverterを渡すようなものですが、詳細はわかりません。

+0

私はそれが良いかどうか尋ねていませんでしたが、あなたとMartjinはそれはそうではないと言いました。もし私が 'public class {public string SomeProperty {get;セット; }} 'あなたはまだそれが悪い考えであり、プロパティ(' someProperty')のためのより低いキャメルケースを好むと感じますか? – dillenmeister

+0

契約にデシリアライズすることを決定した時点で、コーディング規約がすでに存在しています。それにもかかわらず、今日のデフォルトのMVCバインダーは、大文字と小文字を区別しません。 – Aidin

+1

.NETのプロパティの大文字表記法はPascalCaseですが、システムのコンベンションではJSONをcamelCaseにしています。私はPascalCaseに変換するのが最善のアプローチだと思っています。あなたの多くがそれに反対していることにちょうど驚きました。 – dillenmeister

13

私はAvner Shahar-Kashtanに同意します。 特に、JSONを制御できない場合は、を実行しないでください。

つまり、ExpandoObjectとカスタムExpandoObjectConverterを使用して行うことができます。 JSON.NETには既にExpandoObjectConverterが用意されています。

//のコメントは、コードスニペットの中で変更した箇所に表示されています。

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; } 
} 
} 

パスカルケース変換の簡単な文字列です。あなたが必要ならスマートにしてください。

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 

ContractResolverCamelCasePropertyNamesContractResolverバックJSONにオブジェクトをシリアル化するときに使用して再度キャメルケースなるれます。これはJSON.NETでも提供されています。これを必要としない場合は、省略することができます。

+0

これは答えのようです。私はそこより良い拡張ポイントがあることを望んだ...私は今upvoteし、私がそれを試してみたときに答えてマークします。ありがとう! – dillenmeister

+10

優れたコード。私は「あなたはこれをしてはいけない」というコメントに完全に同意しませんが。 jsonがjavascriptクライアントから来ている場合、このコンベンションの不一致が発生する可能性があります。 Javascriptのプロパティの規約はcamelCaseです。プロパティのC#規則はPascalです。それはいつか改造されなければならない。 –

+0

これは優れたソリューションです。私は、パスカルの場合に変換することが許容される方法であるユースケースがあると考えています。私の場合、クラスの1つのプロパティは動的でした。その結果、非動的型の場合はパスカルケース、動的な場合はラクダケース、それはサブプロパティです。また、 'CanConvert'メソッドを 'return(objectType == typeof(Object));'を使用して変更し、動的プロパティへのマッピングを有効にしました。 – gb2d

0

newtonsoftについてあなたの特性にこの属性を追加します。

[JsonProperty("schwabFirmId")] 

シンプルなオプションを使用すると、最大のMongoDBを含むためであれば(あなただけのクラスごとに一度それを実行する必要があるため):MongoDBのへの参照を追加してみてください。 Bson.Serialization.Conventions。

その後、あなたのモデルのコンストラクタでこれを追加します。

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

いずれかがPascalCasedお気に入りのC#のプロパティと、あなたのJSON camelCase形式を維持します。

デシリアライズすると、受信データがPascalCasedとして処理され、シリアライズするとそのデータがcamelCaseに変更されます。