2012-02-12 26 views
14

Đây là những gì tôi có:Pascal trường hợp tính năng động với Json.NET

using Newtonsoft.Json; 

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

này hoạt động tốt:

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

Tôi muốn điều này để làm việc (chữ cái đầu tiên của tài sản trên bên cased) mà không thay đổi json:

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

Trả lời

0

bạn phải thay đổi JSON thành, {\"SomeProperty\":\"some value\"}

+1

Cảm ơn! Tôi không có quyền kiểm soát JSON mặc dù, vì vậy nó không phải là có thể. – dillenmeister

2

Tôi không thể không cảm thấy rằng đây không phải là ý tưởng hay. Có vẻ như bạn đang cố gắng bảo tồn quy ước mã hóa, nhưng với chi phí duy trì sự trung thực giữa định dạng dây (cấu trúc JSON) và các lớp logic của bạn. Điều này có thể gây nhầm lẫn cho các nhà phát triển, những người mong đợi các lớp JSON được giữ nguyên và có thể gây ra các vấn đề nếu bạn cần, hoặc sẽ cần trong tương lai, để sắp xếp lại dữ liệu này vào cùng định dạng JSON.

Điều đó nói rằng, điều này có thể đạt được bằng cách tạo các lớp .NET trước thời hạn, sau đó sử dụng quá tải DeserializeObject(string value, JsonSerializerSettings settings), chuyển số JsonSerializerSettings với bộ thuộc tính Binder. Bạn cũng sẽ cần phải viết một tuỳ chỉnh SerializationBinder trong đó bạn xác định một cách thủ công mối quan hệ giữa lớp JSON của bạn và lớp .NET được xác định trước của bạn.

Có thể tạo các lớp Pascal-cased trong thời gian chạy mà không cần định trước chúng, nhưng tôi chưa từng khắc sâu đủ vào việc thực hiện JSON.NET cho điều đó. Có lẽ một trong các cài đặt JsonSerializerSettings khác, như chuyển một CustomCreationConverter, nhưng tôi không chắc chắn về các chi tiết.

+0

Mặc dù tôi không hỏi liệu có nên làm điều này hay không, cả bạn và Martjin đều nói rằng nó không phải ... vì vậy nó là vì nó 'năng động'? Nếu tôi deserialized để 'public class {public string SomeProperty {get; bộ; }} 'bạn vẫn cảm thấy đó là một ý tưởng tồi và thích trường hợp lạc đà thấp hơn cho thuộc tính (' someProperty')? – dillenmeister

+0

Quy ước mã hóa đã tồn tại khi bạn quyết định deserialize vào một hợp đồng. Tuy nhiên, chất kết dính MVC mặc định ngày nay không phân biệt chữ hoa chữ thường. – Aidin

+1

Quy ước viết hoa cho các thuộc tính trong .NET là PascalCase trong khi quy ước cho hệ thống tôi nhận được JSON từ việc xảy ra là camelCase. Tôi nghĩ việc chuyển đổi sang PascalCase là cách tiếp cận tốt nhất và tôi đã rất ngạc nhiên khi nhiều người trong số các bạn chống lại nó. – dillenmeister

13

Tôi đồng ý với Avner Shahar-Kashtan. Bạn không nên làm điều này, đặc biệt là nếu bạn không kiểm soát được JSON.

Điều đó có nghĩa là có thể thực hiện với việc sử dụng ExpandoObject và tùy chỉnh ExpandoObjectConverter. JSON.NET đã cung cấp ExpandoObjectConverter vì vậy với một số điều chỉnh nhỏ, bạn có những gì bạn muốn.

Lưu ý các số // CHANGED nhận xét bên trong đoạn mã để cho bạn thấy nơi tôi đã thay đổi.

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

Một chuỗi đơn giản để chuyển đổi trường hợp Pascal. Làm cho nó thông minh hơn nếu bạn cần.

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

Bây giờ bạn có thể sử dụng nó như thế này.

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 

Các ContractResolverCamelCasePropertyNamesContractResolver được sử dụng khi tuần tự các đối tượng trở về JSON và làm cho nó trường hợp Camel một lần nữa. Điều này cũng được cung cấp bởi JSON.NET. Nếu bạn không cần điều này, bạn có thể bỏ qua nó.

+0

Ok, có vẻ như đây là câu trả lời! Tôi hy vọng sẽ có một điểm mở rộng tốt hơn so với điều này mặc dù ... Tôi sẽ upvote bây giờ và đánh dấu là trả lời khi tôi đã thử nó ra. Cảm ơn! – dillenmeister

+10

Mã tuyệt vời. Mặc dù tôi hoàn toàn không đồng ý với nhận xét rằng "bạn không nên làm điều này". Nếu json là đến từ một khách hàng javascript, sau đó quy ước này không phù hợp có thể sẽ xảy ra. Quy ước của Javascript cho thuộc tính là camelCase. C# quy ước cho các thuộc tính là Pascal. Nó phải được chuyển đổi đôi khi. –

+0

Đây là một giải pháp tuyệt vời. Tôi tin rằng có những trường hợp sử dụng chuyển đổi sang trường hợp Pascal là thực hành được chấp nhận. Trong trường hợp của tôi, một tài sản trên một lớp học là năng động. Kết quả là trường hợp pascal cho các kiểu không động, và trường hợp lạc đà cho các thuộc tính động và nó phụ. Tôi cũng đã sửa đổi phương thức 'CanConvert' để sử dụng 'return (objectType == typeof (Object));', cho phép ánh xạ tới các thuộc tính động. – gb2d

0

Đối newtonsoft thêm thuộc tính này để tài sản của bạn:

[JsonProperty("schwabFirmId")] 

Một lựa chọn đơn giản hơn (vì bạn chỉ cần làm điều đó một lần cho mỗi lớp) nếu bạn đang lên cho bao gồm MongoDB: hãy thử thêm một tham chiếu đến MongoDB. Bson.Serialization.Conventions.

Sau đó thêm này trong constructor mô hình của bạn:

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

Hoặc ai sẽ giữ tính # C yêu thích của bạn PascalCased và json bạn camelCased.

Deserializing sẽ xử lý dữ liệu đến như PascalCased và serializing sẽ thay đổi thành camelCase.

Các vấn đề liên quan