我正在针对返回JSON数据的第三方API编程,但格式可能有点奇怪。某些属性可以是对象(包含Id属性),也可以是字符串(对象的Id)。例如,以下两个都是有效的:
{
ChildObject: 'childobjectkey1'
}
和
{
ChildObject: {
Id: 'childobjectkey1',
// (other properties)
}
}
我正在尝试使用JSON.net将其反序列化为强类型类,但到目前为止还没有太多运气。我最好的想法是将它序列化为两个属性,一个是字符串,另一个是对象,并为每个属性使用自定义JsonConverter来允许变量行为:
public abstract class BaseEntity
{
public string Id { get; set; }
}
public class ChildObject : BaseEntity { }
public class MyObject
{
[JsonProperty("ChildObject")]
[JsonConverter(typeof(MyCustomIdConverter))]
public string ChildObjectId { get; set; }
[JsonProperty("ChildObject")]
[JsonConverter(typeof(MyCustomObjectConverter))]
public ChildObject ChildObject { get; set; }
}
但是,设置 JsonProperty
具有相同PropertyName的两个属性上的属性会导致异常:
Newtonsoft.Json.JsonSerializationException:具有名称的成员
“ChildObject”已存在于“.....”中。
使用JsonPropertyAttribute指定其他名称。
如果我能克服这个障碍,我相当肯定JsonConverter方法会起作用 - 我怀疑错误是存在的,因为JsonProperty属性用于序列化以及反序列化。在这个例子中,我对序列化这个类没兴趣 - 它只会被用作反序列化的目标。
我无法控制远程端(它是第三方API),但我希望能够实现此序列化。我不介意它是否正在使用我开始使用的方法,或者我还没有想到的方法。
这个问题 也有关系,但没有答案。
试试这个(如果您将在代码中使用它,请通过一些彻底的验证来扩展它):
public class MyObject
{
public ChildObject MyChildObject;
public string MyChildObjectId;
[JsonProperty("ChildObject")]
public object ChildObject
{
get
{
return MyChildObject;
}
set
{
if (value is JObject)
{
MyChildObject = ((JToken)value).ToObject<ChildObject>();
MyChildObjectId = MyChildObject.Id;
}
else
{
MyChildObjectId = value.ToString();
MyChildObject = null;
}
}
}
}
不是为每个字段创建两个单独的转换器,而是为“main”属性创建单个转换器并将另一个转换器链接到它。 ChildObjectId
源于 ChildObject
。
public class MyObject
{
[JsonIgnore]
public string ChildObjectId
{
get { return ChildObject.Id; }
// I would advise against having a setter here
// you should only allow changes through the object only
set { ChildObject.Id = value; }
}
[JsonConverter(typeof(MyObjectChildObjectConverter))]
public ChildObject ChildObject { get; set; }
}
现在转换了 ChildObject
可能有点挑战。对象有两种可能的表示形式:字符串或对象。确定您拥有的表示形式并执行转换。
public class MyObjectChildObjectConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ChildObject);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var obj = serializer.Deserialize<JToken>(reader);
switch (obj.Type)
{
case JTokenType.Object:
return ReadAsObject(obj as JObject);
case JTokenType.String:
return ReadAsString((string)(JValue)obj);
default:
throw new JsonSerializationException("Unexpected token type");
}
}
private object ReadAsObject(JObject obj)
{
return obj.ToObject<ChildObject>();
}
private object ReadAsString(string str)
{
// do a lookup for the actual object or whatever here
return new ChildObject
{
Id = str,
};
}
}
这是我在这种情况下会做的。
- 父子类中只有一个属性用于子对象,并使其为类型
ChildObject
- 创建自定义
JsonConverter
它可以检查JSON和:
- 如果数据存在,则反序列化子对象的完整实例,或者
- 创建子对象的新实例并设置其ID,将所有其他属性保留为空。 (或者您可以像Jeff Mercado建议的那样,让转换器根据ID从数据库加载对象,如果这适用于您的情况。)
- (可选)在子对象上放置一个属性,指示它是否已完全填充。转换器可以在反序列化期间设置此属性。
反序列化后,如果有的话 ChildObject
JSON中的属性(具有ID或完整对象值),保证您拥有 ChildObject
实例,你可以从中获取它的ID;否则,如果没有 ChildObject
JSON中的财产 ChildObject
父类中的属性将为null。
下面是一个完整的工作示例来演示。在这个例子中,我修改了父类以包含三个独立的实例 ChildObject
显示JSON中的不同可能性(仅限字符串ID,完整对象且不存在)。他们都使用相同的转换器。我还加了一个 Name
财产和 IsFullyPopulated
财产到 ChildObject
类。
以下是DTO课程:
public abstract class BaseEntity
{
public string Id { get; set; }
}
public class ChildObject : BaseEntity
{
public string Name { get; set; }
public bool IsFullyPopulated { get; set; }
}
public class MyObject
{
[JsonProperty("ChildObject1")]
[JsonConverter(typeof(MyCustomObjectConverter))]
public ChildObject ChildObject1 { get; set; }
[JsonProperty("ChildObject2")]
[JsonConverter(typeof(MyCustomObjectConverter))]
public ChildObject ChildObject2 { get; set; }
[JsonProperty("ChildObject3")]
[JsonConverter(typeof(MyCustomObjectConverter))]
public ChildObject ChildObject3 { get; set; }
}
这是转换器:
class MyCustomObjectConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ChildObject));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
ChildObject child = null;
if (token.Type == JTokenType.String)
{
child = new ChildObject();
child.Id = token.ToString();
child.IsFullyPopulated = false;
}
else if (token.Type == JTokenType.Object)
{
child = token.ToObject<ChildObject>();
child.IsFullyPopulated = true;
}
else if (token.Type != JTokenType.Null)
{
throw new JsonSerializationException("Unexpected token: " + token.Type);
}
return child;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
以下是演示转换器操作的测试程序:
class Program
{
static void Main(string[] args)
{
string json = @"
{
""ChildObject1"":
{
""Id"": ""key1"",
""Name"": ""Foo Bar Baz""
},
""ChildObject2"": ""key2""
}";
MyObject obj = JsonConvert.DeserializeObject<MyObject>(json);
DumpChildObject("ChildObject1", obj.ChildObject1);
DumpChildObject("ChildObject2", obj.ChildObject2);
DumpChildObject("ChildObject3", obj.ChildObject3);
}
static void DumpChildObject(string prop, ChildObject obj)
{
Console.WriteLine(prop);
if (obj != null)
{
Console.WriteLine(" Id: " + obj.Id);
Console.WriteLine(" Name: " + obj.Name);
Console.WriteLine(" IsFullyPopulated: " + obj.IsFullyPopulated);
}
else
{
Console.WriteLine(" (null)");
}
Console.WriteLine();
}
}
这是上面的输出:
ChildObject1
Id: key1
Name: Foo Bar Baz
IsFullyPopulated: True
ChildObject2
Id: key2
Name:
IsFullyPopulated: False
ChildObject3
(null)