簡體   English   中英

使用Json.NET的Pascal案例動態屬性

[英]Pascal case dynamic properties with 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

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

在將對象序列化回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,但我不確定細節。

對於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。

你必須將你的JSON更改為, {\\"SomeProperty\\":\\"some value\\"}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM