Dieser Imgur-API- Aufruf gibt eine Liste zurück, die sowohl die in JSON dargestellten Klassen " Gallery Image" als auch " Gallery Album" enthält .
Ich kann nicht sehen, wie diese mit Json.NET automatisch deserialisiert werden, da es keine Eigenschaft vom Typ $ gibt, die dem Deserializer mitteilt, welche Klasse dargestellt werden soll. Es gibt eine Eigenschaft namens "IsAlbum", mit der zwischen beiden unterschieden werden kann.
Diese Frage scheint eine Methode zu zeigen, sieht aber wie ein Hack aus.
Wie deserialisiere ich diese Klassen? (mit C #, Json.NET) .
Beispieldaten:
Galerie Bild
{
"id": "OUHDm",
"title": "My most recent drawing. Spent over 100 hours.",
...
"is_album": false
}
Galerie Album
{
"id": "lDRB2",
"title": "Imgur Office",
...
"is_album": true,
"images_count": 3,
"images": [
{
"id": "24nLu",
...
"link": "http://i.imgur.com/24nLu.jpg"
},
{
"id": "Ziz25",
...
"link": "http://i.imgur.com/Ziz25.jpg"
},
{
"id": "9tzW6",
...
"link": "http://i.imgur.com/9tzW6.jpg"
}
]
}
}
c#
serialization
json.net
imgur
Peter Kneale
quelle
quelle
there is no $type property
.Antworten:
Sie können dies ziemlich einfach tun, indem Sie eine benutzerdefinierte
JsonConverter
Datei erstellen , um die Objektinstanziierung zu handhaben. Angenommen, Sie haben Ihre Klassen wie folgt definiert:public abstract class GalleryItem { public string id { get; set; } public string title { get; set; } public string link { get; set; } public bool is_album { get; set; } } public class GalleryImage : GalleryItem { // ... } public class GalleryAlbum : GalleryItem { public int images_count { get; set; } public List<GalleryImage> images { get; set; } }
Sie würden den Konverter folgendermaßen erstellen:
public class GalleryItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(GalleryItem).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); // Using a nullable bool here in case "is_album" is not present on an item bool? isAlbum = (bool?)jo["is_album"]; GalleryItem item; if (isAlbum.GetValueOrDefault()) { item = new GalleryAlbum(); } else { item = new GalleryImage(); } serializer.Populate(jo.CreateReader(), item); return item; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
Hier ist ein Beispielprogramm, das den Konverter in Aktion zeigt:
class Program { static void Main(string[] args) { string json = @" [ { ""id"": ""OUHDm"", ""title"": ""My most recent drawing. Spent over 100 hours."", ""link"": ""http://i.imgur.com/OUHDm.jpg"", ""is_album"": false }, { ""id"": ""lDRB2"", ""title"": ""Imgur Office"", ""link"": ""http://alanbox.imgur.com/a/lDRB2"", ""is_album"": true, ""images_count"": 3, ""images"": [ { ""id"": ""24nLu"", ""link"": ""http://i.imgur.com/24nLu.jpg"" }, { ""id"": ""Ziz25"", ""link"": ""http://i.imgur.com/Ziz25.jpg"" }, { ""id"": ""9tzW6"", ""link"": ""http://i.imgur.com/9tzW6.jpg"" } ] } ]"; List<GalleryItem> items = JsonConvert.DeserializeObject<List<GalleryItem>>(json, new GalleryItemConverter()); foreach (GalleryItem item in items) { Console.WriteLine("id: " + item.id); Console.WriteLine("title: " + item.title); Console.WriteLine("link: " + item.link); if (item.is_album) { GalleryAlbum album = (GalleryAlbum)item; Console.WriteLine("album images (" + album.images_count + "):"); foreach (GalleryImage image in album.images) { Console.WriteLine(" id: " + image.id); Console.WriteLine(" link: " + image.link); } } Console.WriteLine(); } } }
Und hier ist die Ausgabe des obigen Programms:
id: OUHDm title: My most recent drawing. Spent over 100 hours. link: http://i.imgur.com/OUHDm.jpg id: lDRB2 title: Imgur Office link: http://alanbox.imgur.com/a/lDRB2 album images (3): id: 24nLu link: http://i.imgur.com/24nLu.jpg id: Ziz25 link: http://i.imgur.com/Ziz25.jpg id: 9tzW6 link: http://i.imgur.com/9tzW6.jpg
Geige: https://dotnetfiddle.net/1kplME
quelle
Populate
Ansatz anstelle von verwendenToObject
. Siehe die Antworten zu stackoverflow.com/questions/25404202/… und stackoverflow.com/questions/29124126/… . Ich habe hier ein Beispiel für die beiden Ansätze in einem Gist: gist.github.com/chrisoldwood/b604d69543a5fe5896a94409058c7a95 .JsonSerializer.Populate()
nichtJObject.ToObject()
wie von Ivan und Chris vorgeschlagen verwendet wird. Dadurch werden Probleme mit rekursiven Schleifen vermieden und der Konverter kann erfolgreich mit Attributen verwendet werden.Einfach mit JsonSubTypes- Attributen, die mit Json.NET funktionieren
[JsonConverter(typeof(JsonSubtypes), "is_album")] [JsonSubtypes.KnownSubType(typeof(GalleryAlbum), true)] [JsonSubtypes.KnownSubType(typeof(GalleryImage), false)] public abstract class GalleryItem { public string id { get; set; } public string title { get; set; } public string link { get; set; } public bool is_album { get; set; } } public class GalleryImage : GalleryItem { // ... } public class GalleryAlbum : GalleryItem { public int images_count { get; set; } public List<GalleryImage> images { get; set; } }
quelle
@JsonSubTypes
Attribute. Ein weiteres Anwendungsfallbeispiel finden Sie unter stackoverflow.com/a/45447923/863980 ( siehe auch Cage / Animal-Beispiel von @KonstantinPelepelin in Kommentaren).Fortgeschrittene Antwort von Brian Rogers . Und über "Verwenden Sie Serializer.Populate () anstelle von item.ToObject ()". Wenn abgeleitete Typen über Konstruktoren verfügen oder einige von ihnen über einen eigenen benutzerdefinierten Konverter verfügen, müssen Sie die allgemeine Methode zum Deserialisieren von JSON verwenden. Sie müssen also die Arbeit verlassen, um NewtonJson ein neues Objekt zu instanziieren. Auf diese Weise können Sie es in Ihrem CustomJsonConverter erreichen:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { ..... YOU Code For Determine Real Type of Json Record ....... // 1. Correct ContractResolver for you derived type var contract = serializer.ContractResolver.ResolveContract(DeterminedType); if (converter != null && !typeDeserializer.Type.IsAbstract && converter.GetType() == GetType()) { contract.Converter = null; // Clean Wrong Converter grabbed by DefaultContractResolver from you base class for derived class } // Deserialize in general way var jTokenReader = new JTokenReader(jObject); var result = serializer.Deserialize(jTokenReader, DeterminedType); return (result); }
Dies funktioniert, wenn Sie eine Rekursion von Objekten haben.
quelle
Nach der Implementierung sollten Sie die De-Serialisierung durchführen können, ohne die Art und Weise zu ändern, in der Sie Ihre Klassen entworfen haben, und indem Sie ein anderes Feld als $ type verwenden, um zu entscheiden, in was die De-Serialisierung erfolgen soll.
public class GalleryImageConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(GalleryImage) || objectType == typeof(GalleryAlbum)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { try { if (!CanConvert(objectType)) throw new InvalidDataException("Invalid type of object"); JObject jo = JObject.Load(reader); // following is to avoid use of magic strings var isAlbumPropertyName = ((MemberExpression)((Expression<Func<GalleryImage, bool>>)(s => s.is_album)).Body).Member.Name; JToken jt; if (!jo.TryGetValue(isAlbumPropertyName, StringComparison.InvariantCultureIgnoreCase, out jt)) { return jo.ToObject<GalleryImage>(); } var propValue = jt.Value<bool>(); if(propValue) { resultType = typeof(GalleryAlbum); } else{ resultType = typeof(GalleryImage); } var resultObject = Convert.ChangeType(Activator.CreateInstance(resultType), resultType); var objectProperties=resultType.GetProperties(); foreach (var objectProperty in objectProperties) { var propType = objectProperty.PropertyType; var propName = objectProperty.Name; var token = jo.GetValue(propName, StringComparison.InvariantCultureIgnoreCase); if (token != null) { objectProperty.SetValue(resultObject,token.ToObject(propType)?? objectProperty.GetValue(resultObject)); } } return resultObject; } catch (Exception ex) { throw; } } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
quelle
Ich poste dies nur, um die Verwirrung zu beseitigen. Wenn Sie mit einem vordefinierten Format arbeiten und es deserialisieren müssen, hat dies meiner Meinung nach am besten funktioniert und die Mechanik demonstriert, damit andere es nach Bedarf anpassen können.
public class BaseClassConverter : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var j = JObject.Load(reader); var retval = BaseClass.From(j, serializer); return retval; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } public override bool CanConvert(Type objectType) { // important - do not cause subclasses to go through this converter return objectType == typeof(BaseClass); } } // important to not use attribute otherwise you'll infinite loop public abstract class BaseClass { internal static Type[] Types = new Type[] { typeof(Subclass1), typeof(Subclass2), typeof(Subclass3) }; internal static Dictionary<string, Type> TypesByName = Types.ToDictionary(t => t.Name.Split('.').Last()); // type property based off of class name [JsonProperty(PropertyName = "type", Required = Required.Always)] public string JsonObjectType { get { return this.GetType().Name.Split('.').Last(); } set { } } // convenience method to deserialize a JObject public static new BaseClass From(JObject obj, JsonSerializer serializer) { // this is our object type property var str = (string)obj["type"]; // we map using a dictionary, but you can do whatever you want var type = TypesByName[str]; // important to pass serializer (and its settings) along return obj.ToObject(type, serializer) as BaseClass; } // convenience method for deserialization public static BaseClass Deserialize(JsonReader reader) { JsonSerializer ser = new JsonSerializer(); // important to add converter here ser.Converters.Add(new BaseClassConverter()); return ser.Deserialize<BaseClass>(reader); } }
quelle
[FromBody]
Attribut?