C# 选择性地将实例的属性序列化为JSON

C# 选择性地将实例的属性序列化为JSON,c#,json.net,C#,Json.net,我有一些JSON文件,其中包含一种有点奇怪的格式。我必须为我的项目阅读、修改和保存此格式。不幸的是,我对给定的格式没有任何影响 这种格式的奇怪之处在于,该结构中的所有实体都有一个唯一的标识符(无效地)称为“$id”。这些实例也可以通过它们的id而不是它们的完整属性集来引用。在本例中,没有“$id”,而是有一个“$ref”字段,其中包含一个已定义的实体。请看下面的(缩短的)示例: { "$id": "1", "StyleName": "Standard", "Style":

我有一些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#基类实体。这个基类维护一个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;
        }
    }
}
到目前为止,一切顺利。现在回答我的问题:

  • [JsonProperty(“$id”)]
    [JsonProperty($ref”)]
    不起作用,因为$id和$ref不是有效的JSON字段名标识符。目前,我正在使用正则表达式将它们替换为有效的内容,然后将它们重新替换回原来的状态。但这是缓慢的。我想知道我是否可以使用
    NamingStrategy
    来实现同样的目标。但我没有成功。记住,我需要两种方式,反序列化和序列化
  • 序列化是一个更难的问题。如果我按原样序列化我的结构,那么在每个实例中都会有,
    “$id”:
    “$ref”:
    字段。此外,所有其他字段与被引用项一起使用默认值序列化。最简单的方法是在
    JsonSerializerSettings
    中简单地设置
    DefaultValueHandling.IgnoreAndPopulate
    。但这样,所有默认值都会从输出中剥离,从而导致意外行为。我尝试使用
    ContractResolver
    ,但失败了,因为这是当前属性的本地属性,无法考虑周围的实体。有没有其他方法来实现自定义序列化策略

  • 棘手的问题,我知道。还有很多东西需要阅读和理解。但是如果这很容易的话,我就不会把所有的东西都放在这里了。非常需要您的帮助。

    这些
    $id
    s和
    $ref
    s在启用后由Json.NET内部使用。你不需要关心它们,只要定义你的
    RootObject
    StyleRoot
    等类,通常没有
    实体
    基类,并在
    JsonSerializerSettings
    中设置
    PreserveReferencesHandling
    模式。Json.NET将处理引擎盖下的
    $id
    s和
    $ref
    s,并创建一个保留原始引用结构的对象网络:

    var obj = JsonConvert.DeserializeObject<RootObject>(json,
        new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All });
    
    演示:


    注意:在您的输入数据样本中,
    “$ref”
    属性包含一个数值,而Json.NET写入并期望
    “$ref”
    属性具有字符串值。可能是因为您在一些实验过程中无意中发布了更改过的JSON文本吗?

    太完美了!对我有用。关于“$ref”值,您是对的。为了使它更紧凑,我删去了很多文本。我不得不做一些手工编辑。。。顺便问一下,PreserveReferenceHandling.Object和PreserveReferenceHandling.All之间有什么区别?
    var obj = JsonConvert.DeserializeObject<RootObject>(json,
        new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All });
    
    var json = JsonConvert.SerializeObject(obj,
        new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });