Newtonsoft.Json advanced usage

Keywords: JSON

Mobile applications pay attention to fast speed and good experience. There is a performance problem with the server interface of a project at hand, which needs to be optimized. In many modifications of the interface, the entity adds many fields for intermediate calculation or storage, and finally serializes the returned data with Newtonsoft.Json. After analysis, a simple list interface returns 16 fields for each line of data, but the mobile APP only uses 7 fields, and the data of the remaining 9 fields are redundant, If the data returned by the interface is 40K, that is, about 20K data is invalid data. It takes almost 1s to download 20K under 3G network. Not returning invalid data can save at least 1s and greatly improve the user experience. This article will introduce some advanced usage of Newtonsoft.Json. You can modify very little code to solve the above problems.

Reading catalog

Back to the top

Introduction to newtonsoft.jason

During development, a lot of data exchange is transmitted in JSON format. When using JSON, we often involve the use of several serialization objects: datacontractjsonserializer and JavaScript serializer   and  Json.NET Newtonsoft. Jason. Most people will choose Json.NET with good performance and versatility. This is not Microsoft's class library, but an open-source world-class Json operation class library. From the performance comparison below, we can see one of its performance advantages.

Complete API Introduction, easy to use

 

Back to the top

Basic Usage

Json.Net supports serialization and deserialization of DataTable,DataSet,Entity Framework and entity. The following examples illustrate serialization and deserialization respectively.

DataTable:

            //Serialize DataTable
            DataTable dt = new DataTable();
            dt.Columns.Add("Age", Type.GetType("System.Int32"));
            dt.Columns.Add("Name", Type.GetType("System.String"));
            dt.Columns.Add("Sex", Type.GetType("System.String"));
            dt.Columns.Add("IsMarry", Type.GetType("System.Boolean"));
            for (int i = 0; i < 4; i++)
            {
                DataRow dr = dt.NewRow();
                dr["Age"] = i + 1;
                dr["Name"] = "Name" + i;
                dr["Sex"] = i % 2 == 0 ? "male" : "female";
                dr["IsMarry"] = i % 2 > 0 ? true : false;
                dt.Rows.Add(dr);
            }
            Console.WriteLine(JsonConvert.SerializeObject(dt));

Deserialize with the above string

 string json = JsonConvert.SerializeObject(dt);
 dt=JsonConvert.DeserializeObject<DataTable>(json);
 foreach (DataRow dr in dt.Rows)
 {
   Console.WriteLine("{0}\t{1}\t{2}\t{3}\t", dr[0], dr[1], dr[2], dr[3]);
 }

Entity serialization is the same as DataTable, but there is no more introduction.

Back to the top

Advanced Usage

     1. Ignore some attributes

    2. Processing of default values

    3. Processing of null value

    4. Support non-public members

    5. Date processing

    6. Custom serialized field name

7. Dynamically determine whether attributes are serialized

    8. Custom formatting of enumeration values

9. Custom type conversion

  10. Global serialization settings

  1, Ignore some attributes

Similar to the interface optimization introduced at the beginning of this question, some attributes in the entity do not need to be serialized and returned. You can use this feature. First, introduce the Json.Net serialization mode: OptOut and OptIn

OptOut By default, all public members in the class will be serialized. If you don't want to be serialized, you can use the attribute JsonIgnore
OptIn By default, all members will not be serialized. Only members marked with the attribute JsonProperty in the class will be serialized. It is very useful when there are many members in the class, but the client only needs part of the data

 

 

Only the name attribute is required

    [JsonObject(MemberSerialization.OptIn)]
    public class Person
    {
        public int Age { get; set; }

        [JsonProperty]
        public string Name { get; set; }

        public string Sex { get; set; }

        public bool IsMarry { get; set; }

        public DateTime Birthday { get; set; }
    }

  Marriage attribute is not required

    [JsonObject(MemberSerialization.OptOut)]
    public class Person
    {
        public int Age { get; set; }

        public string Name { get; set; }

        public string Sex { get; set; }

        [JsonIgnore]
        public bool IsMarry { get; set; }

        public DateTime Birthday { get; set; }
    }

  As can be seen from the above example, it is very simple to implement the requirement not to return some attributes. 1. Add [jsonobject (memberserialization. Optout)] to the entity class. 2. Add [jsonobject] description to the attribute that does not need to be returned.

2, Default value processing

    If you want to ignore the default value attribute during serialization, you can determine it through JsonSerializerSettings.DefaultValueHandling, which is an enumeration value

DefaultValueHandling.Ignore
Default values are ignored when serializing and deserializing
DefaultValueHandling.Include
Include default values when serializing and deserializing

 

 

 

 [DefaultValue(10)]
 public int Age { get; set; }
 Person p = new Person { Age = 10, Name = "Zhang Sanfeng", Sex = "male", IsMarry = false, Birthday = new DateTime(1991, 1, 2) };
 JsonSerializerSettings jsetting=new JsonSerializerSettings();
 jsetting.DefaultValueHandling=DefaultValueHandling.Ignore;
 Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

The final results are as follows:

 

3, Processing of null values

The attribute with NULL value needs to be ignored during serialization, which can be determined through JsonSerializerSettings.NullValueHandling. In addition, setting the attribute through JsonSerializerSettings takes effect on all attributes in the serialization process. If you want to take effect on a property alone, you can use JsonProperty. The following two methods will be shown respectively

  1.JsonSerializerSettings

 Person p = new Person { room=null,Age = 10, Name = "Zhang Sanfeng", Sex = "male", IsMarry = false, Birthday = new DateTime(1991, 1, 2) };
 JsonSerializerSettings jsetting=new JsonSerializerSettings();
 jsetting.NullValueHandling = NullValueHandling.Ignore;
 Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

   

   2.JsonProperty

Through the method of setting the JsonProperty property, you can realize the special processing requirements of a property, such as default value processing, null value processing, user-defined property name processing and format processing. The above null value processing implementation

 [JsonProperty(NullValueHandling=NullValueHandling.Ignore)]
 public Room room { get; set; }

 

4, Support non-public members

  Public members are processed by default during serialization. If non-public members need to be processed, the attribute "JsonProperty" should be added to the member

 [JsonProperty]
 private int Height { get; set; }

 

5, Date processing

The format of datetime type date is troublesome. The system will format it into iso date standardHowever, in actual use, most of the dates may be in yyyy MM DD or yyyy MM DD HH: mm: SS format. The solution is to change the DateTime type to string type, format it, and then serialize it. If you don't want to modify the code, you can use the following scheme.

      Json.Net provides the IsoDateTimeConverter date conversion class, which can realize the corresponding date conversion through JsnConverter

    [JsonConverter(typeof(IsoDateTimeConverter))]
    public DateTime Birthday { get; set; }

However, the date format of IsoDateTimeConverter is not what we want. We can inherit this class to implement our own date

    public class ChinaDateTimeConverter : DateTimeConverterBase
    {
        private static IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd" };

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            return dtConverter.ReadJson(reader, objectType, existingValue, serializer);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            dtConverter.WriteJson(writer, value, serializer);
        }
    }

    You have implemented a yyyy MM DD format conversion class. You can see that only the date format given when initializing the IsoDateTimeConverter is yyyy mm DD. let's see the effect below

[JsonConverter(typeof(ChinaDateTimeConverter))]
public DateTime Birthday { get; set; }

   You can implement different conversion classes according to your needs

 

6, Custom serialized field name

     The attribute name defined in the entity may not be the name you want, but the entity definition cannot be changed. At this time, you can customize the serialized field name.

     [JsonProperty(PropertyName = "CName")]
     public string Name { get; set; }

 

7, Dynamically determines whether attributes are serialized

This is to meet the increased demand of @ mi Li'er. According to some scenarios, scenario A may output three attributes A, B and C, and scenario B may output attributes E and F. Although this requirement does not necessarily exist in practice, json.net can still support this feature.

Inherit the default DefaultContractResolver class and pass in the properties to be output

     It is rewritten and modified. In most cases, the fields to be excluded are less than the fields to be retained,    In order to facilitate writing, the constructor is modified here, and retain is added to indicate whether props is a field to be reserved or excluded

public class LimitPropsContractResolver : DefaultContractResolver
    {
        string[] props = null;

        bool retain;

        /// <summary>
        ///Constructor
        /// </summary>
        ///< param name = "props" > passed in attribute array < / param >
        ///< param name = "retain" > true: it means props is a field to be reserved; false: it means props is a field to be excluded < / param >
        public LimitPropsContractResolver(string[] props, bool retain=true)
        {
            //Specifies the manifest of attributes to serialize
            this.props = props;

            this.retain = retain;
        }

        protected override IList<JsonProperty> CreateProperties(Type type,

        MemberSerialization memberSerialization)
        {
            IList<JsonProperty> list =
            base.CreateProperties(type, memberSerialization);
            //Keep only the attributes listed in the manifest
            return list.Where(p => {
                if (retain)
                {
                    return props.Contains(p.PropertyName);
                }
                else
                {
                    return !props.Contains(p.PropertyName);
                }      
            }).ToList();
        }
    
        public int Age { get; set; }

        [JsonIgnore]
        public bool IsMarry { get; set; }

        public string Sex { get; set; }
      JsonSerializerSettings jsetting=new JsonSerializerSettings();
      jsetting.ContractResolver = new LimitPropsContractResolver(new string[] { "Age", "IsMarry" });
      Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

  Using a custom parsing class, only "Age" is output,   "IsMarry" two attributes. Look at the final result. Only the Age attribute is output. Why isn't the IsMarry attribute output because it is marked with JsonIgnore

 

  Seeing the above results, it is very simple to realize one part of pc side serialization and the other part of mobile side serialization. Let's change the code to realize it

  string[] propNames = null;
  if (p.Age > 10)
  {
    propNames = new string[] { "Age", "IsMarry" };
  }
  else
  {
      propNames = new string[] { "Age", "Sex" };
  }
  jsetting.ContractResolver = new LimitPropsContractResolver(propNames);
  Console.WriteLine(JsonConvert.SerializeObject(p, Formatting.Indented, jsetting));

 

8, Custom formatting of enumeration values

   By default, the enumeration type in the entity is formatted as the integer value corresponding to the enumeration. What if it needs to be formatted as the character corresponding to the enumeration? Newtonsoft. Jason also helped us think of this. Let's look at an example

    public enum NotifyType
    {
        /// <summary>
        ///Emil send
        /// </summary>
        Mail=0,

        /// <summary>
        ///SMS sending
        /// </summary>
        SMS=1
    }

    public class TestEnmu
    {
        /// <summary>
        ///Message sending type
        /// </summary>
        public NotifyType Type { get; set; }
    }
    JsonConvert.SerializeObject(new TestEnmu());

Output results:  Now modify it and output "Type":"Mail"

    public class TestEnmu
    {
        /// <summary>
        ///Message sending type
        /// </summary>
        [JsonConverter(typeof(StringEnumConverter))]
        public NotifyType Type { get; set; }
    }

Everything else remains the same. A JsonConverter(typeof(StringEnumConverter)) is added to the Type attribute to convert the enumeration value into the corresponding string, and the StringEnumConverter is the built-in conversion Type of Newtonsoft.Json, and the final output result

 

9, Custom type conversion

By default, the Boolean system in the entity is formatted as true or false. How do you change the requirement from true to yes and false to no? We can implement this requirement by custom type conversion. See the example below

public class BoolConvert : JsonConverter
    {
        private string[] arrBString { get; set; }

        public BoolConvert()
        {
            arrBString = "yes,no".Split(',');
        }

        /// <summary>
        ///Constructor
        /// </summary>
        ///< param name = "Boolean string" > string value converted from Boolean value < / param >
        public BoolConvert(string BooleanString)
        {
            if (string.IsNullOrEmpty(BooleanString))
            {
                throw new ArgumentNullException();
            }
            arrBString = BooleanString.Split(',');
            if (arrBString.Length != 2)
            {
                throw new ArgumentException("BooleanString The format does not comply with the regulations");
            }
        }


        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            bool isNullable = IsNullableType(objectType);
            Type t = isNullable ? Nullable.GetUnderlyingType(objectType) : objectType;

            if (reader.TokenType == JsonToken.Null)
            {
                if (!IsNullableType(objectType))
                {
                    throw new Exception(string.Format("Cannot convert null value to {0}.", objectType));
                }

                return null;
            }

            try
            {
                if (reader.TokenType == JsonToken.String)
                {
                    string boolText = reader.Value.ToString();
                    if (boolText.Equals(arrBString[0], StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                    else if (boolText.Equals(arrBString[1], StringComparison.OrdinalIgnoreCase))
                    {
                        return false;
                    }
                }

                if (reader.TokenType == JsonToken.Integer)
                {
                    //numerical value
                    return Convert.ToInt32(reader.Value) == 1;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Error converting value {0} to type '{1}'", reader.Value, objectType));
            }
            throw new Exception(string.Format("Unexpected token {0} when parsing enum", reader.TokenType));
        }

        /// <summary>
        ///Judge whether it is Bool type
        /// </summary>
        ///< param name = "objectType" > type < / param >
        ///< returns > can be converted if it is bool type < / returns >
        public override bool CanConvert(Type objectType)
        {
            return true;
        }


        public bool IsNullableType(Type t)
        {
            if (t == null)
            {
                throw new ArgumentNullException("t");
            }
            return (t.BaseType.FullName=="System.ValueType" && t.GetGenericTypeDefinition() == typeof(Nullable<>));
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value == null)
            {
                writer.WriteNull();
                return;
            }

            bool bValue = (bool)value;

            if (bValue)
            {
                writer.WriteValue(arrBString[0]);
            }
            else
            {
                writer.WriteValue(arrBString[1]);
            }
        }
    }

  The BoolConvert type is customized and inherited from JsonConverter. The constructor parameter Boolean string allows us to customize the conversion of true and false to the corresponding string. Let's see how to use this custom conversion type in the entity

    public class Person
    {
        [JsonConverter(typeof(BoolConvert))]
        public bool IsMarry { get; set; }
    }

'

The corresponding personalized conversion requirements can be realized by using user-defined conversion types.

 

10, Global serialization settings

At the beginning of the article, the problem of how the Null value field does not return is raised, and the corresponding solution is also given in advanced usage, using jsetting.NullValueHandling = NullValueHandling.Ignore; To set not to return Null values. There's a trouble with this. Every serialization that doesn't want to return a Null value needs to be set. Can I set some default values for serialization? Here's the answer

  Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
   JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
   {
    //Date type Default Formatting   setting.DateFormatHandling = Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat; setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    //Null value processing setting.NullValueHandling = NullValueHandling.Ignore;

//Bool type conversion settings in advanced usage 9 setting.Converters.Add(new BoolConvert("yes,no")); return setting; });

 

After this setting, you don't need to set the serialization separately in the future. My favorite setting is null value processing.

Back to the top

summary

The Newtonsoft.Json serialization library has thought of many features for us and implemented many features. In addition to the high-level usages introduced above, there are other special usages that can be learned on the official website. Of course, my favorite feature here is the function of ignoring some attribute serialization. Small code changes optimize the interface and improve the user experience.

 

Source: https://www.cnblogs.com/yanweidie/p/4605212.html

Posted by chrislive on Mon, 29 Nov 2021 05:27:47 -0800