Dav*_*d P 44 c# json json.net deserialization
有些代码(我无法更改)使用Newtonsoft.Json DeserializeObject<T>(strJSONData)
从Web请求中获取数据并将其转换为类对象(我可以更改类).通过使用我的类属性进行装饰,[DataMember(Name = "raw_property_name")]
我可以将原始JSON数据映射到我的类中的正确属性.有没有办法可以将JSON复杂对象的子属性映射到一个简单的属性?这是一个例子:
{
"picture":
{
"id": 123456,
"data":
{
"type": "jpg",
"url": "http://www.someplace.com/mypicture.jpg"
}
}
}
Run Code Online (Sandbox Code Playgroud)
除了URL之外,我不关心任何其他图片对象,因此不想在我的C#类中设置复杂对象.我真的只想要这样的东西:
[DataMember(Name = "picture.data.url")]
public string ProfilePicture { get; set; }
Run Code Online (Sandbox Code Playgroud)
这可能吗?
Bri*_*ers 56
好吧,如果你只需要一个额外的属性,一个简单的方法是将你的JSON解析为a JObject
,ToObject()
用来填充你的类JObject
,然后用来SelectToken()
引入额外的属性.
所以,假设你的课看起来像这样:
class Person
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("age")]
public string Age { get; set; }
public string ProfilePicture { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
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");
Run Code Online (Sandbox Code Playgroud)
小提琴:https://dotnetfiddle.net/7gnJCK
如果您更喜欢更实用的解决方案,则可以进行自定义JsonConverter
以使该JsonProperty
属性的行为与您描述的相似.转换器需要在类级别操作,并使用一些反射结合上述技术来填充所有属性.这是代码中的样子:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
为了演示,让我们假设JSON现在如下所示:
{
"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"
}
}
Run Code Online (Sandbox Code Playgroud)
...除了之前的信息之外,您还对这个人最喜欢的电影(标题和年份)和喜欢的颜色感兴趣.您首先要使用[JsonConverter]
属性标记目标类,以将其与自定义转换器关联,然后[JsonProperty]
在每个属性上使用属性,并指定所需的属性路径(区分大小写)作为名称.目标属性也不必是原语 - 你可以像我在这里一样使用子类Movie
(并注意不需要介入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; }
}
Run Code Online (Sandbox Code Playgroud)
有了所有属性,您可以正常反序列化,它应该"正常工作":
Person p = JsonConvert.DeserializeObject<Person>(json);
Run Code Online (Sandbox Code Playgroud)
小提琴:https://dotnetfiddle.net/Ljw32O
rob*_*a01 13
标记的答案不是100%完成,因为它忽略了可能注册的任何IContractResolver,例如CamelCasePropertyNamesContractResolver等.
同样返回false for can convert会阻止其他用户案例,所以我将其更改为 return objectType.GetCustomAttributes(true).OfType<JsonPathConverter>().Any();
这是更新版本:https: //dotnetfiddle.net/F8C8U8
我还删除了JsonProperty
在链接中说明的设置属性的需要.
如果由于某种原因上面的链接死亡或爆炸,我还包括以下代码:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 8
坚持做
lastLevel [nesting [i]] = new JValue(prop.GetValue (value));
Run Code Online (Sandbox Code Playgroud)
你必须做
lastLevel[nesting[i]] = JValue.FromObject(jValue);
Run Code Online (Sandbox Code Playgroud)
否则我们有一个
无法确定类型的JSON对象类型...
例外
一段完整的代码将是这样的:
object jValue = prop.GetValue(value);
if (prop.PropertyType.IsArray)
{
if(jValue != null)
//https://stackoverflow.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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人需要使用@BrianRogers的JsonPathConverter还与WriteJson
选择,这里有一个解决方案(即仅适用于具有路径作品只点):
删除该CanWrite
属性,以便它true
再次成为默认值.
用WriteJson
以下代码替换代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
如上所述,这仅适用于包含点的路径.鉴于此,您应该添加以下代码ReadJson
以防止其他情况:
[...]
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);
[...]
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
15176 次 |
最近记录: |