Zusammenfassung : Wie ordne ich einen Feldnamen in JSON-Daten einem Feldnamen eines .Net-Objekts zu, wenn JavaScriptSerializer.Deserialize verwendet wird?
Längere Version : Ich habe die folgenden JSON-Daten, die von einer Server-API zu mir kommen (nicht in .Net codiert)
{"user_id":1234, "detail_level":"low"}
Ich habe das folgende C # -Objekt dafür:
[Serializable]
public class DataObject
{
[XmlElement("user_id")]
public int UserId { get; set; }
[XmlElement("detail_level")]
public DetailLevel DetailLevel { get; set; }
}
Wobei DetailLevel eine Aufzählung mit "Niedrig" als einem der Werte ist.
Dieser Test schlägt fehl:
[TestMethod]
public void DataObjectSimpleParseTest()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
Assert.IsNotNull(dataObject);
Assert.AreEqual(DetailLevel.Low, dataObject.DetailLevel);
Assert.AreEqual(1234, dataObject.UserId);
}
Und die letzten beiden Asserts schlagen fehl, da in diesen Feldern keine Daten vorhanden sind. Wenn ich die JSON-Daten in ändere
{"userid":1234, "detaillevel":"low"}
Dann geht es vorbei. Ich kann das Verhalten des Servers jedoch nicht ändern und möchte, dass die Clientklassen gut benannte Eigenschaften in der C # -Sprache haben. Ich kann LINQ nicht für JSON verwenden, da ich möchte, dass es außerhalb von Silverlight funktioniert. Es sieht so aus, als hätten die XmlElement-Tags keine Wirkung. Ich weiß nicht, woher ich die Idee habe, dass sie überhaupt relevant sind, wahrscheinlich nicht.
Wie wird die Feldnamenzuordnung in JavaScriptSerializer durchgeführt? Kann man das überhaupt machen?
quelle
JavaScriptSerializer
.JwtSecurityTokenHandler
Verwendet es über die statischeJsonExtensions.Serializer
Eigenschaft. Wenn Sie es also zur Laufzeit ändern, kann dies Auswirkungen auf anderen Code haben, der erwartet, dass es unverändert bleibt. Viele dieser Klassen sind leider so. :(Antworten:
Ich habe es noch einmal mit der DataContractJsonSerializer- Klasse versucht . Dies löst es:
Der Code sieht folgendermaßen aus:
using System.Runtime.Serialization; [DataContract] public class DataObject { [DataMember(Name = "user_id")] public int UserId { get; set; } [DataMember(Name = "detail_level")] public string DetailLevel { get; set; } }
Und der Test ist:
using System.Runtime.Serialization.Json; [TestMethod] public void DataObjectSimpleParseTest() { DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DataObject)); MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(JsonData)); DataObject dataObject = serializer.ReadObject(ms) as DataObject; Assert.IsNotNull(dataObject); Assert.AreEqual("low", dataObject.DetailLevel); Assert.AreEqual(1234, dataObject.UserId); }
Der einzige Nachteil ist, dass ich DetailLevel von einer Aufzählung in eine Zeichenfolge ändern musste. Wenn Sie den Aufzählungstyp beibehalten, erwartet der DataContractJsonSerializer das Lesen eines numerischen Werts und schlägt fehl. Siehe DataContractJsonSerializer und Aufzählungen für weitere Details.
Meiner Meinung nach ist dies ziemlich schlecht, zumal JavaScriptSerializer es korrekt handhabt. Dies ist die Ausnahme, bei der Sie versuchen, eine Zeichenfolge in eine Aufzählung zu analysieren:
System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type DataObject. The value 'low' cannot be parsed as the type 'Int64'. ---> System.Xml.XmlException: The value 'low' cannot be parsed as the type 'Int64'. ---> System.FormatException: Input string was not in a correct format
Und wenn Sie die Aufzählung so markieren, ändert sich dieses Verhalten nicht:
[DataContract] public enum DetailLevel { [EnumMember(Value = "low")] Low, ... }
Dies scheint auch in Silverlight zu funktionieren.
quelle
Durch Erstellen eines benutzerdefinierten JavaScriptConverter können Sie jeder Eigenschaft einen beliebigen Namen zuordnen. Es erfordert jedoch eine Handcodierung der Karte, was nicht ideal ist.
public class DataObjectJavaScriptConverter : JavaScriptConverter { private static readonly Type[] _supportedTypes = new[] { typeof( DataObject ) }; public override IEnumerable<Type> SupportedTypes { get { return _supportedTypes; } } public override object Deserialize( IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer ) { if( type == typeof( DataObject ) ) { var obj = new DataObject(); if( dictionary.ContainsKey( "user_id" ) ) obj.UserId = serializer.ConvertToType<int>( dictionary["user_id"] ); if( dictionary.ContainsKey( "detail_level" ) ) obj.DetailLevel = serializer.ConvertToType<DetailLevel>( dictionary["detail_level"] ); return obj; } return null; } public override IDictionary<string, object> Serialize( object obj, JavaScriptSerializer serializer ) { var dataObj = obj as DataObject; if( dataObj != null ) { return new Dictionary<string,object> { {"user_id", dataObj.UserId }, {"detail_level", dataObj.DetailLevel } } } return new Dictionary<string, object>(); } }
Dann können Sie wie folgt deserialisieren:
var serializer = new JavaScriptSerializer(); serialzer.RegisterConverters( new[]{ new DataObjectJavaScriptConverter() } ); var dataObj = serializer.Deserialize<DataObject>( json );
quelle
Json.NET macht, was Sie wollen (Haftungsausschluss: Ich bin der Autor des Pakets). Es unterstützt das Lesen von DataContract / DataMember-Attributen sowie von eigenen Attributen, um die Eigenschaftsnamen zu ändern. Es gibt auch die StringEnumConverter-Klasse zum Serialisieren von Enum-Werten als Name und nicht als Nummer.
quelle
Es gibt keine Standardunterstützung für das Umbenennen von Eigenschaften.
JavaScriptSerializer
Sie können jedoch ganz einfach eigene hinzufügen:using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web.Script.Serialization; using System.Reflection; public class JsonConverter : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { List<MemberInfo> members = new List<MemberInfo>(); members.AddRange(type.GetFields()); members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)); object obj = Activator.CreateInstance(type); foreach (MemberInfo member in members) { JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute)); if (jsonProperty != null && dictionary.ContainsKey(jsonProperty.Name)) { SetMemberValue(serializer, member, obj, dictionary[jsonProperty.Name]); } else if (dictionary.ContainsKey(member.Name)) { SetMemberValue(serializer, member, obj, dictionary[member.Name]); } else { KeyValuePair<string, object> kvp = dictionary.FirstOrDefault(x => string.Equals(x.Key, member.Name, StringComparison.InvariantCultureIgnoreCase)); if (!kvp.Equals(default(KeyValuePair<string, object>))) { SetMemberValue(serializer, member, obj, kvp.Value); } } } return obj; } private void SetMemberValue(JavaScriptSerializer serializer, MemberInfo member, object obj, object value) { if (member is PropertyInfo) { PropertyInfo property = (PropertyInfo)member; property.SetValue(obj, serializer.ConvertToType(value, property.PropertyType), null); } else if (member is FieldInfo) { FieldInfo field = (FieldInfo)member; field.SetValue(obj, serializer.ConvertToType(value, field.FieldType)); } } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Type type = obj.GetType(); List<MemberInfo> members = new List<MemberInfo>(); members.AddRange(type.GetFields()); members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0)); Dictionary<string, object> values = new Dictionary<string, object>(); foreach (MemberInfo member in members) { JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute)); if (jsonProperty != null) { values[jsonProperty.Name] = GetMemberValue(member, obj); } else { values[member.Name] = GetMemberValue(member, obj); } } return values; } private object GetMemberValue(MemberInfo member, object obj) { if (member is PropertyInfo) { PropertyInfo property = (PropertyInfo)member; return property.GetValue(obj, null); } else if (member is FieldInfo) { FieldInfo field = (FieldInfo)member; return field.GetValue(obj); } return null; } public override IEnumerable<Type> SupportedTypes { get { return new[] { typeof(DataObject) }; } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class JsonPropertyAttribute : Attribute { public JsonPropertyAttribute(string name) { Name = name; } public string Name { get; set; } }
Die
DataObject
Klasse wird dann:public class DataObject { [JsonProperty("user_id")] public int UserId { get; set; } [JsonProperty("detail_level")] public DetailLevel DetailLevel { get; set; } }
Ich schätze, dass dies etwas spät sein könnte, dachte aber, dass andere Leute, die das
JavaScriptSerializer
eher nutzen wollen als das, esDataContractJsonSerializer
vielleicht zu schätzen wissen.quelle
Erstellen Sie eine von JavaScriptConverter geerbte Klasse. Sie müssen dann drei Dinge implementieren:
Methoden-
Eigentum-
Sie können die JavaScriptConverter-Klasse verwenden, wenn Sie mehr Kontrolle über den Serialisierungs- und Deserialisierungsprozess benötigen.
JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new MyCustomConverter() }); DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
Hier ist ein Link für weitere Informationen
quelle
Ich habe die Verwendung von Newtonsoft.Json wie folgt verwendet. Erstellen Sie ein Objekt:
public class WorklistSortColumn { [JsonProperty(PropertyName = "field")] public string Field { get; set; } [JsonProperty(PropertyName = "dir")] public string Direction { get; set; } [JsonIgnore] public string SortOrder { get; set; } }
Rufen Sie nun die folgende Methode auf, um wie unten gezeigt zum Json-Objekt zu serialisieren.
string sortColumn = JsonConvert.SerializeObject(worklistSortColumn);
quelle
Für diejenigen, die nicht gehen wollen Newtonsoft Json.Net oder
DataContractJsonSerializer
aus irgendeinem Grund (ich nicht irgend :) denken kann), hier ist eine Implementierung ,JavaScriptConverter
dass TrägerDataContract
undenum
zurstring
Umwandlung -public class DataContractJavaScriptConverter : JavaScriptConverter { private static readonly List<Type> _supportedTypes = new List<Type>(); static DataContractJavaScriptConverter() { foreach (Type type in Assembly.GetExecutingAssembly().DefinedTypes) { if (Attribute.IsDefined(type, typeof(DataContractAttribute))) { _supportedTypes.Add(type); } } } private bool ConvertEnumToString = false; public DataContractJavaScriptConverter() : this(false) { } public DataContractJavaScriptConverter(bool convertEnumToString) { ConvertEnumToString = convertEnumToString; } public override IEnumerable<Type> SupportedTypes { get { return _supportedTypes; } } public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { if (Attribute.IsDefined(type, typeof(DataContractAttribute))) { try { object instance = Activator.CreateInstance(type); IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields()) .Concat(type.GetProperties().Where(property => property.CanWrite && property.GetIndexParameters().Length == 0)) .Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute))); foreach (MemberInfo member in members) { DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute)); object value; if (dictionary.TryGetValue(attribute.Name, out value) == false) { if (attribute.IsRequired) { throw new SerializationException(String.Format("Required DataMember with name {0} not found", attribute.Name)); } continue; } if (member.MemberType == MemberTypes.Field) { FieldInfo field = (FieldInfo)member; object fieldValue; if (ConvertEnumToString && field.FieldType.IsEnum) { fieldValue = Enum.Parse(field.FieldType, value.ToString()); } else { fieldValue = serializer.ConvertToType(value, field.FieldType); } field.SetValue(instance, fieldValue); } else if (member.MemberType == MemberTypes.Property) { PropertyInfo property = (PropertyInfo)member; object propertyValue; if (ConvertEnumToString && property.PropertyType.IsEnum) { propertyValue = Enum.Parse(property.PropertyType, value.ToString()); } else { propertyValue = serializer.ConvertToType(value, property.PropertyType); } property.SetValue(instance, propertyValue); } } return instance; } catch (Exception) { return null; } } return null; } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { Dictionary<string, object> dictionary = new Dictionary<string, object>(); if (obj != null && Attribute.IsDefined(obj.GetType(), typeof(DataContractAttribute))) { Type type = obj.GetType(); IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields()) .Concat(type.GetProperties().Where(property => property.CanRead && property.GetIndexParameters().Length == 0)) .Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute))); foreach (MemberInfo member in members) { DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute)); object value; if (member.MemberType == MemberTypes.Field) { FieldInfo field = (FieldInfo)member; if (ConvertEnumToString && field.FieldType.IsEnum) { value = field.GetValue(obj).ToString(); } else { value = field.GetValue(obj); } } else if (member.MemberType == MemberTypes.Property) { PropertyInfo property = (PropertyInfo)member; if (ConvertEnumToString && property.PropertyType.IsEnum) { value = property.GetValue(obj).ToString(); } else { value = property.GetValue(obj); } } else { continue; } if (dictionary.ContainsKey(attribute.Name)) { throw new SerializationException(String.Format("More than one DataMember found with name {0}", attribute.Name)); } dictionary[attribute.Name] = value; } } return dictionary; } }
Hinweis: Dies behandelt
DataContractJavaScriptConverter
nurDataContract
Klassen, die in der Assembly definiert sind, in der sie platziert sind. Wenn Sie Klassen aus separaten Assemblys möchten, ändern Sie die_supportedTypes
Liste im statischen Konstruktionsfehler entsprechend.Dies kann wie folgt verwendet werden:
JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new DataContractJavaScriptConverter(true) }); DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
Die
DataObject
Klasse würde so aussehen -using System.Runtime.Serialization; [DataContract] public class DataObject { [DataMember(Name = "user_id")] public int UserId { get; set; } [DataMember(Name = "detail_level")] public string DetailLevel { get; set; } }
Bitte beachten Sie, dass diese Lösung nicht verarbeitet
EmitDefaultValue
undOrder
Eigenschaften vonDataMember
Attributen unterstützt werden .quelle
JavascriptSerializer
beim Konvertieren von Client-JSON (über Ajax in eine WebMethod) in ein .NET-Objekt, sodass Benutzer wie ich, die an einer Website arbeiten und Client-Daten verarbeiten möchten, keine andere Wahl haben, als die Microsoft-Version zu verwenden. Für die Serialisierung können wir natürlich Newtonsoft verwenden, aber die Deserialisierung erfordert dies. Quelle: referencesource.microsoft.com/#System.Web.Extensions/Script/…Meine Anforderungen umfassten:
Meine Lösung bestand letztendlich darin, SimpleJson ( https://github.com/facebook-csharp-sdk/simple-json ) zu verwenden.
Obwohl Sie es über ein Nuget-Paket installieren können, habe ich nur diese einzelne SimpleJson.cs-Datei (mit der MIT-Lizenz) in mein Projekt aufgenommen und darauf verwiesen.
Ich hoffe das hilft jemandem.
quelle