visual una que propiedades objetos instanciar ejemplos declaración crear clases clase atributos atributo agregar c# json json.net deserialization

que - instanciar una clase c#



¿Puedo especificar una ruta en un atributo para asignar una propiedad en mi clase a una propiedad secundaria en mi JSON? (4)

Bueno, si solo necesita una propiedad adicional única, un enfoque simple es analizar su JSON a un JObject , use ToObject() para completar su clase desde JObject y luego use SelectToken() para extraer la propiedad adicional.

Entonces, suponiendo que su clase se vea así:

class Person { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("age")] public string Age { get; set; } public string ProfilePicture { get; set; } }

Podrías hacer esto:

string json = @" { ""name"" : ""Joe Shmoe"", ""age"" : 26, ""picture"": { ""id"": 123456, ""data"": { ""type"": ""jpg"", ""url"": ""http://www.someplace.com/mypicture.jpg"" } } }"; JObject jo = JObject.Parse(json); Person p = jo.ToObject<Person>(); p.ProfilePicture = (string)jo.SelectToken("picture.data.url");

Violín: https://dotnetfiddle.net/7gnJCK

Si prefiere una solución más elegante, puede crear un JsonConverter personalizado para permitir que el atributo JsonProperty comporte como usted describe. El convertidor necesitaría operar a nivel de clase y usar un poco de reflexión combinada con la técnica anterior para completar todas las propiedades. Así es como se vería en el código:

class JsonPathConverter : JsonConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); object targetObj = Activator.CreateInstance(objectType); foreach (PropertyInfo prop in objectType.GetProperties() .Where(p => p.CanRead && p.CanWrite)) { JsonPropertyAttribute att = prop.GetCustomAttributes(true) .OfType<JsonPropertyAttribute>() .FirstOrDefault(); string jsonPath = (att != null ? att.PropertyName : prop.Name); JToken token = jo.SelectToken(jsonPath); if (token != null && token.Type != JTokenType.Null) { object value = token.ToObject(prop.PropertyType, serializer); prop.SetValue(targetObj, value, null); } } return targetObj; } public override bool CanConvert(Type objectType) { // CanConvert is not called when [JsonConverter] attribute is used return false; } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }

Para demostrarlo, supongamos que el JSON ahora se ve así:

{ "name": "Joe Shmoe", "age": 26, "picture": { "id": 123456, "data": { "type": "jpg", "url": "http://www.someplace.com/mypicture.jpg" } }, "favorites": { "movie": { "title": "The Godfather", "starring": "Marlon Brando", "year": 1972 }, "color": "purple" } }

... y está interesado en la película favorita de la persona (título y año) y el color favorito, además de la información de antes. Primero marcaría su clase de destino con un atributo [JsonConverter] para asociarla con el convertidor personalizado, luego usaría los atributos [JsonProperty] en cada propiedad, especificando la ruta de propiedad deseada ( [JsonProperty] entre mayúsculas y minúsculas) como el nombre. Las propiedades de destino tampoco tienen que ser primitivas: puede usar una clase secundaria como hice aquí con Movie (y observe que no se requiere una clase de Favorites ).

[JsonConverter(typeof(JsonPathConverter))] class Person { [JsonProperty("name")] public string Name { get; set; } [JsonProperty("age")] public int Age { get; set; } [JsonProperty("picture.data.url")] public string ProfilePicture { get; set; } [JsonProperty("favorites.movie")] public Movie FavoriteMovie { get; set; } [JsonProperty("favorites.color")] public string FavoriteColor { get; set; } } // Don''t need to mark up these properties because they are covered by the // property paths in the Person class class Movie { public string Title { get; set; } public int Year { get; set; } }

Con todos los atributos en su lugar, puede simplemente deserializarse de manera normal y debería "funcionar":

Person p = JsonConvert.DeserializeObject<Person>(json);

Violín: https://dotnetfiddle.net/Ljw32O

Hay un código (que no puedo cambiar) que usa DeserializeObject DeserializeObject<T>(strJSONData) para tomar datos de una solicitud web y convertirlos en un objeto de clase (puedo cambiar la clase). Al decorar las propiedades de mi clase con [DataMember(Name = "raw_property_name")] puedo asignar los datos JSON sin procesar a la propiedad correcta en mi clase. ¿Hay alguna manera de asignar la propiedad secundaria de un objeto complejo JSON a una propiedad simple? Aquí hay un ejemplo:

{ "picture": { "id": 123456, "data": { "type": "jpg", "url": "http://www.someplace.com/mypicture.jpg" } } }

No me importa nada del resto del objeto de imagen, excepto la URL, por lo que no quiero configurar un objeto complejo en mi clase C #. Realmente solo quiero algo como:

[DataMember(Name = "picture.data.url")] public string ProfilePicture { get; set; }

es posible?


En lugar de hacer

lastLevel [nesting [i]] = new JValue(prop.GetValue (value));

Tu tienes que hacer

lastLevel[nesting[i]] = JValue.FromObject(jValue);

De lo contrario tenemos un

No se pudo determinar el tipo de objeto JSON para el tipo ...

excepción

Un código completo sería este:

object jValue = prop.GetValue(value); if (prop.PropertyType.IsArray) { if(jValue != null) //https://.com/a/20769644/249895 lastLevel[nesting[i]] = JArray.FromObject(jValue); } else { if (prop.PropertyType.IsClass && prop.PropertyType != typeof(System.String)) { if (jValue != null) lastLevel[nesting[i]] = JValue.FromObject(jValue); } else { lastLevel[nesting[i]] = new JValue(jValue); } }


La respuesta marcada no está 100% completa ya que ignora cualquier IContractResolver que pueda registrarse, como CamelCasePropertyNamesContractResolver, etc.

También devolver false para can convert evitará otros casos de usuario, así que lo cambié para return objectType.GetCustomAttributes(true).OfType<JsonPathConverter>().Any();

Aquí está la versión actualizada: https://dotnetfiddle.net/F8C8U8

También JsonProperty la necesidad de establecer una JsonProperty en una propiedad como se ilustra en el enlace.

Si por alguna razón el enlace de arriba muere o explota, también incluyo el siguiente código:

public class JsonPathConverter : JsonConverter { /// <inheritdoc /> public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); object targetObj = Activator.CreateInstance(objectType); foreach (PropertyInfo prop in objectType.GetProperties().Where(p => p.CanRead && p.CanWrite)) { JsonPropertyAttribute att = prop.GetCustomAttributes(true) .OfType<JsonPropertyAttribute>() .FirstOrDefault(); string jsonPath = att != null ? att.PropertyName : prop.Name; if (serializer.ContractResolver is DefaultContractResolver) { var resolver = (DefaultContractResolver)serializer.ContractResolver; jsonPath = resolver.GetResolvedPropertyName(jsonPath); } if (!Regex.IsMatch(jsonPath, @"^[a-zA-Z0-9_.-]+$")) { throw new InvalidOperationException($"JProperties of JsonPathConverter can have only letters, numbers, underscores, hiffens and dots but name was ${jsonPath}."); // Array operations not permitted } JToken token = jo.SelectToken(jsonPath); if (token != null && token.Type != JTokenType.Null) { object value = token.ToObject(prop.PropertyType, serializer); prop.SetValue(targetObj, value, null); } } return targetObj; } /// <inheritdoc /> public override bool CanConvert(Type objectType) { // CanConvert is not called when [JsonConverter] attribute is used return objectType.GetCustomAttributes(true).OfType<JsonPathConverter>().Any(); } /// <inheritdoc /> public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var properties = value.GetType().GetRuntimeProperties().Where(p => p.CanRead && p.CanWrite); JObject main = new JObject(); foreach (PropertyInfo prop in properties) { JsonPropertyAttribute att = prop.GetCustomAttributes(true) .OfType<JsonPropertyAttribute>() .FirstOrDefault(); string jsonPath = att != null ? att.PropertyName : prop.Name; if (serializer.ContractResolver is DefaultContractResolver) { var resolver = (DefaultContractResolver)serializer.ContractResolver; jsonPath = resolver.GetResolvedPropertyName(jsonPath); } var nesting = jsonPath.Split(''.''); JObject lastLevel = main; for (int i = 0; i < nesting.Length; i++) { if (i == nesting.Length - 1) { lastLevel[nesting[i]] = new JValue(prop.GetValue(value)); } else { if (lastLevel[nesting[i]] == null) { lastLevel[nesting[i]] = new JObject(); } lastLevel = (JObject)lastLevel[nesting[i]]; } } } serializer.Serialize(writer, main); } }


Si alguien necesita usar el JsonPathConverter de @BrianRogers también con la opción WriteJson , aquí hay una solución (que solo funciona para rutas con puntos solamente ):

Elimine la propiedad CanWrite para que vuelva a ser true por defecto.

Reemplace el código WriteJson por lo siguiente:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var properties = value.GetType().GetRuntimeProperties ().Where(p => p.CanRead && p.CanWrite); JObject main = new JObject (); foreach (PropertyInfo prop in properties) { JsonPropertyAttribute att = prop.GetCustomAttributes(true) .OfType<JsonPropertyAttribute>() .FirstOrDefault(); string jsonPath = (att != null ? att.PropertyName : prop.Name); var nesting=jsonPath.Split(new[] { ''.'' }); JObject lastLevel = main; for (int i = 0; i < nesting.Length; i++) { if (i == nesting.Length - 1) { lastLevel [nesting [i]] = new JValue(prop.GetValue (value)); } else { if (lastLevel [nesting [i]] == null) { lastLevel [nesting [i]] = new JObject (); } lastLevel = (JObject)lastLevel [nesting [i]]; } } } serializer.Serialize (writer, main); }

Como dije anteriormente, esto solo funciona para rutas que contienen puntos . Dado eso, debe agregar el siguiente código a ReadJson para evitar otros casos:

[...] string jsonPath = (att != null ? att.PropertyName : prop.Name); if (!Regex.IsMatch(jsonPath, @"^[a-zA-Z0-9_.-]+$")) { throw new InvalidOperationException("JProperties of JsonPathConverter can have only letters, numbers, underscores, hiffens and dots."); //Array operations not permitted } JToken token = jo.SelectToken(jsonPath); [...]