Serialization 如何递归调用WriteJson?

Serialization 如何递归调用WriteJson?,serialization,json.net,jsonserializer,Serialization,Json.net,Jsonserializer,我使用Json.Net。 当我序列化一个Department2对象并调用WriteJson()时,我希望它与Telephone2对象一起递归调用,就像我在ReadJson()中所做的那样 我该怎么做 using System; using Newtonsoft.Json; using Newtonsoft.Json.Linq; public interface ISimpleDatabag { string Databag { get; set; } } [JsonConverter

我使用Json.Net。 当我序列化一个
Department2
对象并调用
WriteJson()
时,我希望它与
Telephone2
对象一起递归调用,就像我在
ReadJson()
中所做的那样

我该怎么做

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public interface ISimpleDatabag
{
    string Databag { get; set; }
}

[JsonConverter(typeof(JsonDataBagCreationConverter<Department2>))]
public class Department2
{
    public Telephone2[] Phones { get; set; }
}

[JsonConverter(typeof(JsonDataBagCreationConverter<Telephone2>))]
public class Telephone2
{
    public string Name { get; set; }
    public string AreaCode { get; set; }
    public string Number { get; set; }
}

public class JsonDataBagCreationConverter<T> : JsonConverter where T : new()
{
    // Json.Net version 4.5.7.15008
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // When I serialize Department and this function is invoked
        // I want it to recursively invoke WriteJson with each of the Telephone objects
        // Like I do in ReadJson
        // How do I do that?
        T t = (T)value;
        serializer.Serialize(writer, t.GetType());
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jsonObject = JObject.Load(reader);
        var target = Create(objectType, jsonObject);
        serializer.Populate(jsonObject.CreateReader(), target); // Will call this function recursively for any objects that have JsonDataBagCreationConverter as attribute
       return target;
    }

    protected T Create(Type objectType, JObject jsonObject)
    {
        return new T();
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }   
}


 private void Form1_Load(object sender, EventArgs e)
        {
            string jsonInput = "{\"Name\": \"Seek4\" , \"CustomDepartmentData\": \"This is custom department data\", \"Phones\":[ {\"Name\": \"A\", \"AreaCode\":444, \"Number\":11111111} ,{\"Name\": \"B\", \"AreaCode\":555, \"Number\":987987987}, {\"Name\": \"C\", \"AreaCode\":222, \"Number\":123123123, \"CustomPhoneData\": \"This is custom phone data\"} ] }";
            Department2 objDepartment2 = JsonConvert.DeserializeObject<Department2>(jsonInput); // Yes, it works well
            Array.Reverse(objDepartment2.Phones);
            string jsonNoDatabag = JsonConvert.SerializeObject(objDepartment2);
        }
使用系统;
使用Newtonsoft.Json;
使用Newtonsoft.Json.Linq;
公共接口ISimpleDatabag
{
字符串数据包{get;set;}
}
[JsonConverter(类型(JsonDataBagCreationConverter))]
公共课部2
{
公用电话2[]电话{get;set;}
}
[JsonConverter(类型(JsonDataBagCreationConverter))]
公共电话2
{
公共字符串名称{get;set;}
公共字符串区域代码{get;set;}
公共字符串编号{get;set;}
}
公共类JsonDataBagCreationConverter:JsonConverter其中T:new()
{
//Json.Net版本4.5.7.15008
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
//当我序列化部门并调用此函数时
//我希望它对每个电话对象递归调用WriteJson
//就像我在ReadJson中做的一样
//我该怎么做?
T=(T)值;
serializer.Serialize(writer,t.GetType());
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
var jsonObject=JObject.Load(读卡器);
var target=Create(objectType,jsonObject);
serializer.Populate(jsonObject.CreateReader(),target);//将递归调用此函数,用于将JsonDataBagCreationConverter作为属性的任何对象
回报目标;
}
受保护的T创建(类型objectType,JObject jsonObject)
{
返回新的T();
}
公共覆盖布尔CanConvert(类型objectType)
{
返回typeof(T).IsAssignableFrom(objectType);
}   
}
私有void Form1\u加载(对象发送方、事件参数e)
{
字符串jsonInput=“{\'Name\”:\“Seek4\”,\“CustomDepartmentData\”:\“这是自定义部门数据\”,\“Phones\”:[{\'Name\:“A\”,\“AreaCode\”:444,\“Number\”:11111111},{\'Name\:“B\”,“AreaCode\”:555,\“Number\”:987987987},{\'Name\:“C\”,“AreaCode\”,“AreaCode\”:222,\“Number\:123123:“Customphone\”这是自定义电话数据:}”;
department2objdepartment2=JsonConvert.DeserializeObject(jsonInput);//是的,它工作得很好
Array.Reverse(objdepart2.Phones);
字符串jsonNoDatabag=JsonConvert.SerializeObject(objDepartment2);
}

最后我自己控制了整个过程,使用了这个巨大的(不是重构的)函数。 我基本上是研究要序列化的对象的每个属性,然后逐个属性序列化它。 然后我可以在每个属性上进行自定义操作

/// <summary>
/// Serializes an object by merging its current values into its databag and returns the databag
    /// </summary>
    /// <param name="objectToSerialize"></param>
    /// <returns>the current values merged into the original databag</returns>
    /// <remarks>Jan Nielsen, 01-10-2012</remarks>
    internal static string SerializeObjectToDatabag(object objectToSerialize)
    {
        // You have to do it property by property instead of just serializing the entire object and merge it into the original
        // because the object might contain lists of objects with custom data and these list might have been sorted differently from when they were loaded
        // So you cannot merge them properly unless you do it on a per listitem basis.
        // Which is what I do here.
        try
        {

            if (objectToSerialize == null) // If you ie serialize an empty object in an array
            {
                return null;
            }

            string updatedDatabag = "";
            bool isIDataBag = objectToSerialize is IDataBag;
            if (isIDataBag)
            {
                updatedDatabag = ((IDataBag)objectToSerialize).Data == null ? "" : ((IDataBag)objectToSerialize).Data.ToString();
                //  updatedDatabag = ((IDataBag)objectToSerialize).Data.ToString(); // Save original data in a local variable. This is the one we will merge new values into
            }
            string result = "";
            // Now iterate through the objects properties
            // Possible properties:
            // Simple types: string, int, bool etc: their current value should be overwritten in the databag
            // types that implement IDatabag: they should be sent to this function recursively so their possible customdata is not overwritten
            // but instead their simple values are merged into their own databag. Then the result of this single property merge is overwritten in the outer objects databag
            // Types that are not simple and don't implement IDatabag but have properties that implement IDatabag
            // types that are not simple and don't implement IDatabag and don't have any properties in any depth that implement IDatabag: They are overwritten in the databag
            // Types that are arrays: 
            //      If the types in the array are simple types (string, bool etc) the entire array property is overwritten in the databag
            //      If the types in the array implement IDatabag each object is sent recursively to this function and their databag is updated via merge
            //      Then the entire array is overwritten in the outer objects databag
            // Types that are generic list are treated like arrays

            var properties = objectToSerialize.GetType().GetProperties();

            // In order to be able to deserialize abstract classes and interfaces, we need to serialize the classname with the class
            // the deserializer recognizes the word $type followed by a type, when its is invoked with a serializerSettings of 
            // serializerSettings.TypeNameHandling = TypeNameHandling.Objects;
            string name = objectToSerialize.GetType().AssemblyQualifiedName;
            string shortName = RemoveAssemblyDetails(name);
            bool addedType = false;

            foreach (var propertyInfo in properties)
            {
                if (propertyInfo.Name.ToLower() != "data") // Just skip Databag. Databag is not a "real" property but the contents of all the properties when the object was loaded + possible custom data
                {
                    if (!addedType)
                    {
                        string jsonSingleProperty = "{ " + ToCustomJson("$type") + " : " + ToCustomJson(shortName) + " }";
                        // Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties) 
                        // and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
                        // and the function will now use the updated databag to merge the other properties into
                        updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
                        addedType = true;
                    }
                    // propertyInfo.Name.ToLower().Contains("struct")
                    var value = propertyInfo.GetValue(objectToSerialize, null); // This gets the value of the specified property in the current object

                    isIDataBag = value is IDataBag; // Update for the current object. Note that ie an array of IDatabag will return false here, because array is not IsimpleDatabag

                    // Basically we should just check if the property implements IDatabag
                    // But the simpletype check is faster because I don't have to check for the interfaces on ie a string, int etc.
                    // This branch takes care of 3 cases:
                    // 1) it is a simple type, ie int
                    // 2) value is null
                    // 3) it is an array with a value of null
                    // If an array with values enters this branch of code the values of the array will be appended, overwritten
                    // Therefore arrays are treated below in a special case. Unless they are null
                    // GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) returns true on ie string[], but only arrays with a value of null should be handled here

                    // This first check originally just checked for simple types
                    // Then it became extended simple types ie non-simple types that only contains simple types ie List<int,int>
                    // But not arrays that must be handled separately
                    // Then it also handled null values 
                    // And then a special case was made for arrays that are null

                    if ((GeneralFunctions.IsExtendedSimpleType_AllTypes(propertyInfo.PropertyType) || value == null) && (!propertyInfo.PropertyType.IsArray || (propertyInfo.PropertyType.IsArray && value == null)))
                    {
                        // You have to merge even though it is default value.
                        // If you have ie a bool that has an initial value of true and you deliberately sets it to false
                        // You want the defaultvalue of false to be merged into the json.

                        string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}"; // ie {"Name" : "Seek4Cars"}
                        // Merge the current value (jsonSingleProperty) into the databag (that might already have been updated with the values of other properties) 
                        // and update the current result with the new values. Ie "Name" : "Seek4" is updated to "Name" : "Seek4Cars" in the databag
                        // and the function will now use the updated databag to merge the other properties into
                        updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
                        continue;
                    }

                    if (isIDataBag) // ie PhoneSingle. A single property of type IDataBag
                    {
                        // Invoke recursively

                        // First check if this is an object with all null values
                        bool allPropertiesAreNull = true; // Maybe this should in the future be expanded with a check on if the property has its default value ie an int property with a value of 0
                        foreach (var propertyInfoLocal in value.GetType().GetProperties())
                        {
                            var valueLocal = propertyInfoLocal.GetValue(value, null);
                            if (valueLocal != null)
                            {
                                allPropertiesAreNull = false;
                                break;
                            }
                        }
                        var testjson = "";
                        if (allPropertiesAreNull)
                        {
                            result = "{" + ToCustomJson(propertyInfo.Name) + " : " + " { } }";
                        }
                        else
                        {
                            testjson = ToCustomJson(value);
                            result = "{" + ToCustomJson(propertyInfo.Name) + " : " + SerializeObjectToDatabag(value) + "}";
                        }

                        updatedDatabag = MergeDefault(result, updatedDatabag, true);
                        continue;
                    }

                    bool containsIDataBag = CheckForDatabagInterfaces.ImplementsInterface(propertyInfo.PropertyType, "idatabag"); // Check if anything inside the property implements IDatabag ie an array of IDatabag

                    if (containsIDataBag)
                    {
                        // Check if it is somekind of generic list (List<T>, Dictionary<T,T) etc) and if it is a type of ignoreTypes ie List<entity>)
                        if (value.GetType().IsGenericType && value.GetType().GetGenericArguments().Length > 0)
                        {
                            string listValuesAsJson = "";
                            if (value is IEnumerable)
                            {
                                listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : [";
                                bool containsItems = false;
                                foreach (var element in (IEnumerable)value)
                                {
                                    containsItems = true;
                                    var current = SerializeObjectToDatabag(element);
                                    if (current != null) // If you serialize an empty array element it is null
                                    {
                                        listValuesAsJson += current + ", "; // Add , between each element
                                    }
                                }
                                if (containsItems)
                                {
                                    listValuesAsJson = listValuesAsJson.Substring(0, listValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
                                }
                                else // No items in value
                                {
                                    listValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
                                }
                            }
                            else // A single, generic KeyValuePair property
                            {
                                listValuesAsJson += "{ " + ToCustomJson(propertyInfo.Name) + " : ";
                                listValuesAsJson += SerializeObjectToDatabag(value);
                                listValuesAsJson += " }";
                            }

                            updatedDatabag = MergeDefault(listValuesAsJson, updatedDatabag, false);
                        }
                        else if (value.GetType().IsArray)
                        {
                            string arrayValuesAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
                            bool containsItems = false;
                            foreach (var element in (Array)value)
                            {
                                // Treat them the same way you treat any other object 
                                var current = SerializeObjectToDatabag(element);
                                if (current != null) // If you serialize an empty array element it is null
                                {
                                    containsItems = true;
                                    arrayValuesAsJson += current + ", ";
                                }
                            }
                            if (containsItems)
                            {
                                arrayValuesAsJson = arrayValuesAsJson.Substring(0, arrayValuesAsJson.Length - 2) + "] }"; // remove last , and add ending ] for the array and add a } because this property is flowing in the free
                            }
                            else // No items in value
                            {
                                arrayValuesAsJson += "] }"; // add ending ] for the array and add a } because this property is flowing in the free
                            }
                            updatedDatabag = MergeDefault(arrayValuesAsJson, updatedDatabag, false);
                        }
                        else if ( value.GetType().BaseType != null && value.GetType().BaseType.FullName.ToLower().Contains("system.collections.objectmodel"))
                        {
                            // This branch was made specifically to take care of the Media collection of a Seek4.Entities.V2.Media.MediaCollection
                            var genericList = (IList)value;
                            int counter = genericList.Count;

                            string listAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : [";
                            if (counter == 0)
                            {
                                listAsJson += "] }"; // Ie { "Media": [] }
                            }
                            else
                            {
                                foreach (var obj in genericList)
                                {
                                    var current = SerializeObjectToDatabag(obj);
                                    listAsJson += current + ", ";
                                }
                                listAsJson = listAsJson.Substring(0, listAsJson.Length -2) + " ] }" ; 
                            }
                            updatedDatabag = MergeDefault(listAsJson, updatedDatabag, true); // hvordan gør json.net dette med standard?

                        }
                        else // a single Non-IDatabag property that contains Idatabag properties
                        {
                            string tempResult = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
                            tempResult += SerializeObjectToDatabag(value) + " }";
                            updatedDatabag = MergeDefault(tempResult, updatedDatabag, true);
                        }
                    }
                    else
                    {
                        if (value.GetType().IsArray) // This is an array of simple types so just overwrite
                        {
                            string arrayAsJson = "{ " + ToCustomJson(propertyInfo.Name) + " : ";
                            arrayAsJson += ToCustomJson(value) + "}";
                            updatedDatabag = MergeDefault(arrayAsJson, updatedDatabag, false);
                        }
                        else // ie an object that is not simpledatabag and doesn't contain simple databag
                        {
                            string jsonSingleProperty = "{" + ToCustomJson(propertyInfo.Name) + " : " + ToCustomJson(value) + "}";
                            updatedDatabag = MergeDefault(jsonSingleProperty, updatedDatabag, true);
                        }
                    }
                }
            }
            return updatedDatabag;
        }
        catch (Exception ex)
        {
            string message = ex.Message;
            string stack = ex.StackTrace;
            throw;
        }
    }



internal static string ToCustomJson(object objectToConvertToJson)
    {
        try
        {
            // Distinguished from Mongodb.Bson.ToJson() extensionmethod by Custom name
            JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
            serializerSettings.TypeNameHandling = TypeNameHandling.Objects; // Adds a $type on all objects which we need when it is abstract classes and interfaces
            IgnoreDataMemberContractResolver contractResolver = new IgnoreDataMemberContractResolver(null, true, true);
            serializerSettings.ContractResolver = contractResolver;
            serializerSettings.DefaultValueHandling = DefaultValueHandling.Ignore;
            IsoDateTimeConverter converter = new IsoDateTimeConverter();
            serializerSettings.Converters.Add(converter);
            string result = JsonConvert.SerializeObject(objectToConvertToJson, Formatting.None, serializerSettings);
            return result;
        }
        catch (Exception ex)
        {
            throw new Exception("Error in ToCustomJson: " + ex.Message, ex);
        }
    }
//
///通过将对象的当前值合并到其数据包来序列化对象,并返回数据包
/// 
/// 
///当前值合并到原始数据包中
///Jan Nielsen,01-10-2012
内部静态字符串序列化ObjectToDataBag(对象objectToSerialize)
{
//您必须逐个属性进行操作,而不是仅仅序列化整个对象并将其合并到原始对象中
//因为对象可能包含具有自定义数据的对象列表,并且这些列表的排序可能与加载时不同
//因此,除非基于每个列表项进行合并,否则无法正确合并它们。
//这就是我在这里做的。
尝试
{
if(objectToSerialize==null)//如果在数组中序列化空对象
{
返回null;
}
字符串updatedDatabag=“”;
bool-isIDataBag=objectToSerialize是IDataBag;
如果(isIDataBag)
{
updatedDatabag=((IDataBag)objectToSerialize.Data==null?”:((IDataBag)objectToSerialize.Data.ToString();
//UpdateDataBag=((IDataBag)objectToSerialize).Data.ToString();//将原始数据保存在局部变量中。这是我们将新值合并到的数据
}
字符串结果=”;
//现在遍历对象属性
//可能的属性:
//简单类型:string、int、bool等:它们的当前值应该在数据包中被覆盖
//实现IDatabag的类型:应该递归地将它们发送到此函数,以便不会覆盖它们可能的customdata
//但是它们的简单值被合并到它们自己的数据库中,然后这个单一属性合并的结果在外部对象数据库中被覆盖
//不简单且不实现IDatabag但具有实现IDatabag的属性的类型
//不简单且未实现IDatabag且在任何深度都没有实现IDatabag的属性的类型:它们在数据包中被覆盖
//是数组的类型:
//如果数组中的类型是简单类型(字符串、bool等),则整个数组属性将在数据库中覆盖
//如果数组中的类型实现IDatabag,则每个对象将递归地发送到此函数,并通过合并更新其数据包
//然后在外部对象数据库中覆盖整个数组
//属于泛型列表的类型被视为数组
var properties=objectToSerialize.GetType().GetProperties();
//为了能够反序列化抽象类和接口,我们需要用类序列化类名
//当使用serializerSettings调用其时,反序列化程序识别紧跟类型的单词$type
//serializerSettings.TypeNameHandling=TypeNameHandling.Object;
字符串名称=objectToSerialize.GetType().Asse