帕斯卡尔情况下的动态性能与Json.NET卡尔、帕斯、情况下、性能

2023-09-04 00:37:55 作者:傻子也說愛

这是我有:

 使用Newtonsoft.Json;

VAR JSON ={\someProperty \:\一定的价值\};
动态反序列化= JsonConvert.DeserializeObject(JSON);
 

这工作得很好:

  Assert.That(deserialized.someProperty.ToString(),Is.EqualTo(某些价值));
 
.NET平台迄今为止性能最佳的Json序列化库

我想这个工作(性能大写的首字母)不改变 JSON

  Assert.That(deserialized.SomeProperty.ToString(),Is.EqualTo(某些价值));
 

解决方案

我同意阿夫纳沙哈尔-卡什坦。 您不应该这样做,,特别是如果你有过的JSON的控制。

这就是说,它可以与使用 ExpandoObject 和一个自定义的ExpandoObjectConverter. JSON.NET已经提供了ExpandoObjectConverter所以有一些小的调整,你有你想要的。

注意在 // CHANGED 的code段内的意见,看看你在哪里我改变了它。

 公共类CamelCaseToPascalCaseExpandoObjectConverter:JsonConverter
{
  // CHANGED
  //该ExpandoObjectConverter需要这个内部方法,所以我们不得不将它复制
  //从JsonReader.cs
  内部静态布尔IsPrimitiveToken(JsonToken令牌)
  {
      开关(标记)
      {
          案例JsonToken.Integer:
          案例JsonToken.Float:
          案例JsonToken.String:
          案例JsonToken.Boolean:
          案例JsonToken.Null:
          案例JsonToken.Undefined:
          案例JsonToken.Date:
          案例JsonToken.Bytes:
              返回true;
          默认:
              返回false;
      }
  }

///<总结>
///将对象的JSON再presentation。
///< /总结>
///< PARAM NAME =作家>的<看到CREF =JsonWriter/>写上述< /参数>
///<参数名称=值>该值小于/参数>
///< PARAM NAME =串行>将调用串行< /参数>
公众覆盖无效WriteJson(JsonWriter作家,对象的值,JsonSerializer串行)
{
  //可以写设置为false
}

///<总结>
///读取对象的JSON重新presentation。
///< /总结>
///< PARAM NAME =读者>的<看到CREF =JsonReader/>从阅读< /参数>
///< PARAM NAME =的objectType>对象的类型< /参数>
///< PARAM NAME =existingValue>对象的现有值被读取< /参数>
///< PARAM NAME =串行>将调用串行< /参数>
///<返回>将对象值小于。/回报>
公众覆盖对象ReadJson(JsonReader读者,类型的objectType,反对existingValue,JsonSerializer串行)
{
  返回ReadValue(读卡器);
}

私有对象ReadValue(JsonReader阅读器)
{
  而(reader.TokenType == JsonToken.Comment)
  {
    如果(!reader.Read())
      抛出新的异常(意外结束。);
  }

  开关(reader.TokenType)
  {
    案例JsonToken.StartObject:
      返回的readObject(读卡器);
    案例JsonToken.StartArray:
      返回ReadList(读卡器);
    默认:
      // CHANGED
      //调用这个类中声明静态方法
      如果(IsPrimitiveToken(reader.TokenType))
        返回reader.Value;

      // CHANGED
      //使用的String.Format内JSON.NET宣布一些util的函数,而不是
      抛出新的异常(的String.Format(CultureInfo.InvariantCulture,转换ExpandoObject时意外令牌:{0},reader.TokenType));
  }
}

私有对象ReadList(JsonReader阅读器)
{
  IList的<对象>名单=新的名单,其中,对象>();

  而(reader.Read())
  {
    开关(reader.TokenType)
    {
      案例JsonToken.Comment:
        打破;
      默认:
        对象V = ReadValue(读卡器);

        list.Add(五);
        打破;
      案例JsonToken.EndArray:
        返回列表;
    }
  }

  抛出新的异常(意外结束。);
}

私有对象的readObject(JsonReader阅读器)
{
  IDictionary的<字符串,对象> expandoObject =新ExpandoObject();

  而(reader.Read())
  {
    开关(reader.TokenType)
    {
      案例JsonToken.PropertyName:
        // CHANGED
        //为ToPascalCase扩展方法添加的呼叫
        字符串参数propertyName = reader.Value.ToString()ToPascalCase()。

        如果(!reader.Read())
          抛出新的异常(意外结束。);

        对象V = ReadValue(读卡器);

        expandoObject [propertyName的] = V;
        打破;
      案例JsonToken.Comment:
        打破;
      案例JsonToken.EndObject:
        返回expandoObject;
    }
  }

  抛出新的异常(意外结束。);
}

///<总结>
///确定此实例是否可以将指定的对象类型。
///< /总结>
///< PARAM NAME =的objectType>对象的类型< /参数>
///<返回>
///&其中c取代;真&所述; / c取代;如果这种情况下可以将指定的对象类型;否则,c为C>假< / c取代。
///< /回报>
公众覆盖布尔CanConvert(类型的objectType)
{
  返回(的objectType == typeof运算(ExpandoObject));
}

///<总结>
///获取一个值,指示是否该<见CREF =JsonConverter/>可以写JSON。
///< /总结>
///<值GT;
///&其中c取代;真&所述; / c取代;如果该<见CREF =JsonConverter/>可以写JSON;否则,c为C>假< / c取代。
///< /值GT;
公众覆盖布尔CanWrite
{
  获得{返回false; }
}
}
 

一个简单的字符串Pascal大小写转换器。让它更聪明,如果你需要。

 公共静态类StringExtensions
{
    公共静态字符串ToPascalCase(这个字符串s)
    {
        如果(string.IsNullOrEmpty(多个)||!char.IsLower(S [0]))
            返回S;

        字符串str = char.ToUpper(S [0],CultureInfo.InvariantCulture)的ToString((的IFormatProvider)CultureInfo.InvariantCulture);

        如果(s.Length→1)
            海峡= STR + s.Substring(1);

        返回海峡;
    }
}
 

现在你可以使用它是这样的。

  VAR设置=新JsonSerializerSettings()
                   {
                       ContractResolver =新CamelCasePropertyNamesContractResolver()
                       转换器=新的名单,其中,JsonConverter> {新CamelCaseToPascalCaseExpandoObjectConverter()}
                   };

VAR JSON ={\someProperty \:\一定的价值\};

动态反序列化= JsonConvert.DeserializeObject< ExpandoObject>(JSON,设置);

Console.WriteLine(deserialized.SomeProperty); //一些价值

VAR json2 = JsonConvert.SerializeObject(反序列化,Formatting.None,设置);

Console.WriteLine(JSON == json2); //真正
 

ContractResolver CamelCasePropertyNamesContractResolver是序列化回JSON对象时使用,并再次使骆驼情况。这也由JSON.NET提供。如果您不需要这个,你可以忽略它。

This is what I have:

using Newtonsoft.Json;

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

This works fine:

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

I want this to work (first letter of properties upper-cased) without changing json:

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

解决方案

I agree with Avner Shahar-Kashtan. You shouldn't be doing this, especially if you have no control over the JSON.

That said, it can be done with the use of a ExpandoObject and a custom ExpandoObjectConverter. JSON.NET already provides a ExpandoObjectConverter so with some little adjustments you have what you want.

Notice the //CHANGED comments inside the code snippet to show you where I changed it.

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

A simple string to Pascal Case converter. Make it smarter if you need to.

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

Now you can use it like this.

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

The ContractResolver CamelCasePropertyNamesContractResolver is used when serializing the object back to JSON and makes it Camel case again. This is also provided by JSON.NET. If you don't need this you can omit it.