Pascal case dynamic properties with Json.NET 使用Json.NET进行Pascal case动态属性

16 浏览
0 Comments

Pascal case dynamic properties with Json.NET 使用Json.NET进行Pascal case动态属性

这是我的代码:

使用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"));

0
0 Comments

问题的出现原因是作者希望保留代码约定,但这会导致在JSON结构和逻辑类之间失去一致性。这可能会让开发人员感到困惑,因为他们期望保留JSON类,并且如果将来需要将数据重新序列化为相同的JSON格式,可能会出现问题。

解决方法是提前创建.NET类,然后使用带有设置的`DeserializeObject(string value, JsonSerializerSettings settings)`重载方法。需要将`JsonSerializerSettings`的`Binder`属性设置为自定义的`SerializationBinder`,在其中手动定义JSON类和预定义.NET类之间的关系。

也许可以在运行时生成帕斯卡命名的类,而不需要预先定义它们,但作者并没有深入研究JSON.NET的实现。可能可以通过使用`JsonSerializerSettings`的其他设置项之一(例如传递CustomCreationConverter)来实现,但作者不确定具体细节。

作者询问是否因为使用了`dynamic`类型而被认为是一个不好的想法。如果反序列化为`public class { public string SomeProperty { get; set; } }`,你是否仍然认为这是一个不好的想法,并且更喜欢属性使用小写驼峰命名(`someProperty`)。

回答是,这个问题的出现不只是因为使用了`dynamic`类型。问题在于在反序列化为契约时已经存在的编码约定。尽管如此,目前默认的MVC绑定器是不区分大小写的。

.NET中属性的大写约定是PascalCase,而我从JSON中获取的系统的约定是camelCase。我认为转换为PascalCase是最好的方法,我只是对你们中的很多人反对感到惊讶。

0
0 Comments

Pascal case dynamic properties with Json.NET(使用Json.NET实现帕斯卡命名法动态属性)问题的出现是因为需要将C#属性命名为帕斯卡命名法(Pascal case),而将JSON数据命名为小驼峰命名法(camel case)。解决方法是通过使用Json.NET提供的属性和MongoDB.Bson.Serialization.Conventions来实现。

对于Json.NET,可以通过在属性上添加[JsonProperty("schwabFirmId")]特性来指定属性的JSON名称。

另一个更简单的方法是,如果你愿意引入MongoDB,可以尝试添加对MongoDB.Bson.Serialization.Conventions的引用。然后在模型的构造函数中添加以下代码:

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

以上方法都可以保持C#属性的帕斯卡命名法,同时将JSON数据转换为小驼峰命名法。反序列化时,将传入的数据视为帕斯卡命名法,而序列化时会将其转换为小驼峰命名法。

0
0 Comments

Pascal case dynamic properties with Json.NET

在使用Json.NET处理JSON数据时,有时候需要将动态属性转换为Pascal Case(帕斯卡命名法)。下面是一种实现此功能的方法:

首先,需要使用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;
    }
}

有了以上的代码,就可以将动态属性转换为Pascal Case了。

使用示例代码如下:

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.NET提供的CamelCasePropertyNamesContractResolver将对象序列化为JSON时,属性名会被转换为驼峰命名法。如果不需要这个功能,可以省略这个设置。

以上就是实现将动态属性转换为Pascal Case的方法,希望对你有所帮助。

0