我有一些JSON文件包含一些奇怪的格式。我必须为我的项目阅读,修改和保存这种格式。不幸的是,我对给定的格式没有任何影响。
这种格式的奇怪之处在于,此结构中的所有实体都具有唯一标识符,该标识符(无效地)称为“$ id”。这些实例也可以通过它们的id来引用,而不是它们的完整属性集。在这种情况下,没有“$ id”,而是有一个“$ ref”字段,包含已定义的实体。请看下面的(缩短的)示例:
{
"$id": "1",
"StyleName": "Standard",
"Style": {
"$id": "2",
"ShapeStyle": {
"$id": "3",
"Background": {
"$id": "4",
"Color": {
"$id": "5",
"A": 255,
"R": 68,
"G": 84,
"B": 106
}
}
},
"RightEndCapsStyle": {
"$id": "6",
"Background": {
"$id": "7",
"Color": {
"$ref": "5"
}
}
}
}
}
为了解决这个问题,我为所有JSON数据类创建了一个C#基类 Entity 。此基类维护一个id注册表,以便它可以轻松找到给定 $ ref 的实例。这是代码:
public class Entity
{
public static Dictionary<string, Entity> Registry = new Dictionary<string, Entity>();
private string key = string.Empty;
[JsonProperty("$id", Order = -2)]
[DefaultValue("")]
public string Key
{
get { return key; }
set
{
key = value;
if (!string.IsNullOrEmpty(key)) Registry.Add(key, this);
}
}
[JsonProperty("$ref")]
public string RefKey { get; set; }
[JsonIgnore]
public bool IsReference => !string.IsNullOrEmpty(RefKey);
[JsonIgnore]
public Entity RefEntity
{
get
{
Entity entity = null;
if (IsReference && !Registry.TryGetValue(RefKey, out entity))
{
throw new ApplicationException("Referenced entity not found!");
}
return entity;
}
}
}
JSON类层次结构如下所示:
public class RootObject: Entity
{
public string StyleName { get; set; }
public StyleRoot Style { get; set; }
}
public class StyleRoot: Entity
{
public Style ShapeStyle { get; set; }
public Style RightEndCapsStyle { get; set; }
}
public class Style: Entity
{
public Background Background { get; set; }
}
public class Background: Entity
{
public Color Color { get; set; }
}
public class Color: Entity
{
public int A { get; set; }
public int R { get; set; }
public int G { get; set; }
public int B { get; set; }
}
到目前为止,这么好。现在问我的问题:
[JsonProperty("$id")]
和[JsonProperty("$ref")]
不起作用,因为 $ id 且 $ ref 不是有效的JSON字段名称标识符。目前,我正在使用正则表达式将它们替换为有效的东西,然后将它们重新替换回原来的状态。但这很迟钝。我想知道我是否可以使用NamingStrategy
来实现同样的目标。但我没有成功。请记住,我需要两种方式,反序列化和序列化。"$id":
和"$ref":
字段。更重要的是,与引用的项目一起,所有其他字段都使用默认值序列化。最简单的方法是在DefaultValueHandling.IgnoreAndPopulate
中设置JsonSerializerSettings
。但是这样,所有默认值都会从输出中删除,从而导致意外行为。我尝试使用ContractResolver
但失败了,因为这是当前属性的本地属性,并且无法考虑周围的实体。有没有其他方法可以实现自定义序列化策略?我知道很难的问题。还有很多东西需要阅读和理解。但如果它很容易,我就不会把精力放在这里。迫切需要你的帮助。
答案 0 :(得分:1)
启用PreserveReferencesHandling
mode时,Json.NET会在内部使用这些$id
和$ref
。您无需关心它们,只需定义RootObject
,StyleRoot
等类,通常不使用Entity
基类,并在{{1}中设置PreserveReferencesHandling
模式}}。 Json.NET将处理引擎盖下的JsonSerializerSettings
和$id
并创建一个保留原始引用结构的对象网络:
$ref
序列化也可以在保留引用处理模式下完成:
var obj = JsonConvert.DeserializeObject<RootObject>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All });
演示:https://dotnetfiddle.net/TygMDZ
N.B。在输入数据示例中,var json = JsonConvert.SerializeObject(obj,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
属性包含数值,而Json.NET写入并期望"$ref"
属性具有字符串值。可能是因为你在一些实验过程中不小心发布了JSON文本吗?