private static void WriteValue(object obj, JsonWriter writer, bool privateWriter, int depth) { if (depth > maxNestingDepth) { throw new JsonException(string.Format("Max allowed object depth reached while trying to export from type {0}", obj.GetType())); } if (obj == null) { writer.Write(null); return; } if (obj is IJsonWrapper) { if (privateWriter) { writer.TextWriter.Write(((IJsonWrapper)obj).ToJson()); } else { ((IJsonWrapper)obj).ToJson(writer); } return; } if (obj is string) { writer.Write((string)obj); return; } if (obj is double) { writer.Write((double)obj); return; } if (obj is long) { writer.Write((long)obj); return; } if (obj is bool) { writer.Write((bool)obj); return; } if (obj is Array) { writer.WriteArrayStart(); Array arr = (Array)obj; Type elemType = arr.GetType().GetElementType(); foreach (object elem in arr) { // if the collection contains polymorphic elements, we need to include type information for deserialization //if (writer.TypeHinting && elem != null & elem.GetType() != elemType) { // writer.WriteObjectStart(); // writer.WritePropertyName(writer.HintTypeName); // writer.Write(elem.GetType().FullName); // writer.WritePropertyName(writer.HintValueName); // WriteValue(elem, writer, privateWriter, depth + 1); // writer.WriteObjectEnd(); //} else { WriteValue(elem, writer, privateWriter, depth + 1); //} } writer.WriteArrayEnd(); return; } if (obj is IList) { writer.WriteArrayStart(); IList list = (IList)obj; // collection might be non-generic type like Arraylist Type elemType = typeof(object); if (list.GetType().GetGenericArguments().Length > 0) { // collection is a generic type like List<T> elemType = list.GetType().GetGenericArguments()[0]; } foreach (object elem in list) { // if the collection contains polymorphic elements, we need to include type information for deserialization //if (writer.TypeHinting && elem != null && elem.GetType() != elemType) { // writer.WriteObjectStart(); // writer.WritePropertyName(writer.HintTypeName); // writer.Write(elem.GetType().AssemblyQualifiedName); // writer.WritePropertyName(writer.HintValueName); // WriteValue(elem, writer, privateWriter, depth + 1); // writer.WriteObjectEnd(); //} else { WriteValue(elem, writer, privateWriter, depth + 1); //} } writer.WriteArrayEnd(); return; } if (obj is IDictionary) { writer.WriteObjectStart(); IDictionary dict = (IDictionary)obj; // collection might be non-generic type like Hashtable Type elemType = typeof(object); if (dict.GetType().GetGenericArguments().Length > 1) { // collection is a generic type like Dictionary<T, V> elemType = dict.GetType().GetGenericArguments()[1]; } foreach (DictionaryEntry entry in dict) { writer.WritePropertyName((string)entry.Key); // if the collection contains polymorphic elements, we need to include type information for deserialization //if (writer.TypeHinting && entry.Value != null && entry.Value.GetType() != elemType) { // writer.WriteObjectStart(); // writer.WritePropertyName(writer.HintTypeName); // writer.Write(entry.Value.GetType().AssemblyQualifiedName); // writer.WritePropertyName(writer.HintValueName); // WriteValue(entry.Value, writer, privateWriter, depth + 1); // writer.WriteObjectEnd(); //} else { WriteValue(entry.Value, writer, privateWriter, depth + 1); //} } writer.WriteObjectEnd(); return; } Type objType = obj.GetType(); // Try a base or custom importer if one exists ExporterFunc exporter = GetExporter(objType); if (exporter != null) { exporter(obj, writer); return; } // Last option, let's see if it's an enum if (obj is Enum) { Type enumType = Enum.GetUnderlyingType(objType); if (enumType == typeof(long)) { writer.Write((long)obj); } else { ExporterFunc enumConverter = GetExporter(enumType); if (enumConverter != null) { enumConverter(obj, writer); } } return; } // Okay, it looks like the input should be exported as an object ObjectMetadata tdata = AddObjectMetadata(objType); writer.WriteObjectStart(); //在Json的第一个字段前插入类型标识 if (writer.TypeHinting) { writer.WriteType(objType); } foreach (string property in tdata.Properties.Keys) { PropertyMetadata pdata = tdata.Properties[property]; // Don't serialize soft aliases (which get added to ObjectMetadata.Properties twice). if (pdata.Alias != null && property != pdata.Info.Name && tdata.Properties.ContainsKey(pdata.Info.Name)) { continue; } // Don't serialize a field or property with the JsonIgnore attribute with serialization usage if (pdata.Ignore) { continue; } if (pdata.IsField) { FieldInfo info = (FieldInfo)pdata.Info; if (pdata.Alias != null) { writer.WritePropertyName(pdata.Alias); } else { writer.WritePropertyName(info.Name); } object value = info.GetValue(obj); //if (writer.TypeHinting && value != null && info.FieldType != value.GetType()) { // // the object stored in the field might be a different type that what was declared, need type hinting // writer.WriteObjectStart(); // writer.WritePropertyName(writer.HintTypeName); // writer.Write(value.GetType().AssemblyQualifiedName); // writer.WritePropertyName(writer.HintValueName); // WriteValue(value, writer, privateWriter, depth + 1); // writer.WriteObjectEnd(); //} else { WriteValue(value, writer, privateWriter, depth + 1); //} } //暂时去除类属性的序列化操作,因为静态数据里面带有不需要序列化的属性 //else { // PropertyInfo info = (PropertyInfo)pdata.Info; // if (info.CanRead) { // if (pdata.Alias != null) { // writer.WritePropertyName(pdata.Alias); // } else { // writer.WritePropertyName(info.Name); // } // object value = info.GetValue(obj, null); // //if (writer.TypeHinting && value != null && info.PropertyType != value.GetType()) { // // // the object stored in the property might be a different type that what was declared, need type hinting // // writer.WriteObjectStart(); // // writer.WritePropertyName(writer.HintTypeName); // // writer.Write(value.GetType().AssemblyQualifiedName); // // writer.WritePropertyName(writer.HintValueName); // // WriteValue(value, writer, privateWriter, depth + 1); // // writer.WriteObjectEnd(); // //} else { // WriteValue(value, writer, privateWriter, depth + 1); // //} // } //} } writer.WriteObjectEnd(); }
private static ObjectMetadata AddObjectMetadata(Type type) { if (objectMetadata.ContainsKey(type)) { return(objectMetadata[type]); } ObjectMetadata data = new ObjectMetadata(); if (type.GetInterface("System.Collections.IDictionary") != null) { data.IsDictionary = true; } data.Properties = new Dictionary <string, PropertyMetadata>(); // Get all kinds of declared properties BindingFlags pflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach (PropertyInfo pinfo in type.GetProperties(pflags)) { if (pinfo.Name == "Item") { ParameterInfo[] parameters = pinfo.GetIndexParameters(); if (parameters.Length != 1) { continue; } if (parameters[0].ParameterType == typeof(string)) { data.ElementType = pinfo.PropertyType; } continue; } // Include properties automatically that have at least one public accessor bool autoInclude = (pinfo.GetGetMethod() != null && pinfo.GetGetMethod().IsPublic) || (pinfo.GetSetMethod() != null && pinfo.GetSetMethod().IsPublic); // If neither accessor is public and we don't have a [JsonInclude] attribute, skip it if (!autoInclude && pinfo.GetCustomAttributes(typeof(JsonInclude), true).Count() == 0) { continue; } PropertyMetadata pdata = new PropertyMetadata(); pdata.Info = pinfo; pdata.Type = pinfo.PropertyType; object[] ignoreAttrs = pinfo.GetCustomAttributes(typeof(JsonIgnore), true).ToArray(); if (ignoreAttrs.Length > 0) { pdata.Ignore = true; } object[] aliasAttrs = pinfo.GetCustomAttributes(typeof(JsonAlias), true).ToArray(); if (aliasAttrs.Length > 0) { JsonAlias aliasAttr = (JsonAlias)aliasAttrs[0]; if (aliasAttr.Alias == pinfo.Name) { throw new JsonException(string.Format("Alias name '{0}' must be different from the property it represents for type '{1}'", pinfo.Name, type)); } if (data.Properties.ContainsKey(aliasAttr.Alias)) { throw new JsonException(string.Format("'{0}' already contains the property or alias name '{1}'", type, aliasAttr.Alias)); } pdata.Alias = aliasAttr.Alias; if (aliasAttr.AcceptOriginal) { data.Properties.Add(pinfo.Name, pdata); } } if (pdata.Alias != null) { data.Properties.Add(pdata.Alias, pdata); } else { if (data.Properties.ContainsKey(pinfo.Name)) { throw new JsonException(string.Format("'{0}' already contains the property or alias name '{1}'", type, pinfo.Name)); } data.Properties.Add(pinfo.Name, pdata); } } // Get all kinds of declared fields BindingFlags fflags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach (FieldInfo finfo in type.GetFields(fflags)) { // If the field isn't public and doesn't have an [Include] attribute, skip it if (!finfo.IsPublic && finfo.GetCustomAttributes(typeof(JsonInclude), true).Count() == 0) { continue; } PropertyMetadata pdata = new PropertyMetadata(); pdata.Info = finfo; pdata.IsField = true; pdata.Type = finfo.FieldType; object[] ignoreAttrs = finfo.GetCustomAttributes(typeof(JsonIgnore), true).ToArray(); if (ignoreAttrs.Length > 0) { pdata.Ignore = true; } object[] aliasAttrs = finfo.GetCustomAttributes(typeof(JsonAlias), true).ToArray(); if (aliasAttrs.Length > 0) { JsonAlias aliasAttr = (JsonAlias)aliasAttrs[0]; if (aliasAttr.Alias == finfo.Name) { throw new JsonException(string.Format("Alias name '{0}' must be different from the field it represents for type '{1}'", finfo.Name, type)); } if (data.Properties.ContainsKey(aliasAttr.Alias)) { throw new JsonException(string.Format("'{0}' already contains the field or alias name '{1}'", type, aliasAttr.Alias)); } pdata.Alias = aliasAttr.Alias; if (aliasAttr.AcceptOriginal) { data.Properties.Add(finfo.Name, pdata); } } if (pdata.Alias != null) { data.Properties.Add(pdata.Alias, pdata); } else { if (data.Properties.ContainsKey(finfo.Name)) { throw new JsonException(string.Format("'{0}' already contains the field or alias name '{1}'", type, finfo.Name)); } data.Properties.Add(finfo.Name, pdata); } } objectMetadata.Add(type, data); return(data); }