【问题标题】:How to "inline" a property in resulting json with json.net如何使用 json.net 在生成的 json 中“内联”属性
【发布时间】:2016-11-29 09:50:55
【问题描述】:

我的一个类中有一个属性,我试图用 json.net 序列化它,我想“内联”,意思是,我不想让该属性嵌套到一个元素中属性名称,但其内容直接在其父级中。

这是一个例子,假设我有以下类结构:

public interface ISteeringWheelIdentifier {}

public interface ISteeringWheel 
{
    ISteeringWheelIdentifier Identifier {get;}
}

public class ManufacturerIdentifier : ISteeringWheelIdentifier
{
    public string ManufacturerEmail {get; set;}
}

public class PartNumberIdentifier : ISteeringWheelIdentifier
{
    public string PartNumber {get; set;}
}

public class ClassicSteeringWheel : ISteeringWheel 
{
    public ClassicSteeringWheel(ManufacturerIdentifier identifier)
    {
        Identifier = identifier;
    }

    public string HornButtonManufacturer {get; set;}

    public ISteeringWheelIdentifier Identifier {get;private set;}
}

public class ModernSteeringWheel : ISteeringWheel
{
    public ModernSteeringWheel(PartNumberIdentifier identifier)
    {
        Identifier = identifier;
    }

    public string TouchpadManufacturer {get; set;}

    public ISteeringWheelIdentifier Identifier {get;private set;}
}

public class Car 
{
    public string CarBrand {get; set;}
    public ISteeringWheel SteeringWheel {get; set;}

}

如果我尝试使用以下代码序列化两个测试对象:

public static void Main()
{
    var car1 = new Car{CarBrand="Ford", SteeringWheel = new ModernSteeringWheel(new PartNumberIdentifier{PartNumber = "123456"})};
    var json = JsonConvert.SerializeObject(car1, Formatting.Indented);
    Console.WriteLine(json);

    var car2 = new Car{CarBrand="Toyota", SteeringWheel = new ClassicSteeringWheel(new ManufacturerIdentifier{ManufacturerEmail = "test@demo.com"})};
    json = JsonConvert.SerializeObject(car2, Formatting.Indented);
    Console.WriteLine(json);
}

你会得到这个结果:

{
  "CarBrand": "Ford",
  "SteeringWheel": {
    "TouchpadManufacturer": null,
    "Identifier": {
      "PartNumber": "123456"
    }
  }
}
{
  "CarBrand": "Toyota",
  "SteeringWheel": {
    "HornButtonManufacturer": null,
    "Identifier": {
      "ManufacturerEmail": "test@demo.com"
    }
  }
}

但是,就我而言,标识符只是一种管理识别方向盘的不同方式的方法,我不需要拥有该属性。我期望的结果 Json 如下:

{
  "CarBrand": "Ford",
  "SteeringWheel": {
    "TouchpadManufacturer": null
    "PartNumber": "123456"
  }
}
{
  "CarBrand": "Toyota",
  "SteeringWheel": {
    "HornButtonManufacturer": null,
    "ManufacturerEmail": "test@demo.com"
  }
}

显然,我可以通过在 ISteeringWheel 中同时包含 ManufacturerEmailPartNumber 并放置一个或另一个 null 并设置为忽略空值来做到这一点,但我宁愿将事物正确分开我的课。

我在这里用上面的代码创建了一个小提琴:https://dotnetfiddle.net/C9RPy9

【问题讨论】:

    标签: c# .net json json.net


    【解决方案1】:

    一种方法是为 ISteeringWheelIdentifier 创建您自己的自定义 json 反序列化器,您应该在其中为每个方向盘标识符类型实现所需的反序列化结果(请参阅http://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm)作为示例。然后,您应该为您的 Identifier 属性 [JsonConverter(typeof([Name of your new converter]))] 设置一个 JsonConverter 属性,它将按照您的指定进行反序列化。

    编辑 - 实际实现它时,要获得所需的行为有点棘手。您需要创建的转换器是用于 ISteeringWheel 接口的转换器。在其中,遍历所有属性,直到找到标识符属性,并处理其序列化。举个例子:

        public class SteeringWheelJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(ISteeringWheel).IsAssignableFrom(objectType);
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            JObject jo = new JObject();
            Type type = value.GetType();
    
            foreach (var prop in type.GetProperties())
            {
                if (prop.CanRead)
                {
                    var propVal = prop.GetValue(value, null);
                    if (prop.Name == "Identifier")
                    {
                        // Iterate over all properties of the identifier, but don't add the identifier object itself 
                        // to the serialized result.
                        Type identifierType = propVal.GetType();
                        foreach (var identifierProp in identifierType.GetProperties())
                        {
                            var identifierPropVal = identifierProp.GetValue(propVal, null);
                            jo.Add(identifierProp.Name, identifierPropVal != null ? JToken.FromObject(identifierPropVal, serializer) : null);
                        }
                    }
                    else
                    {
                        // Add the property to the serialized result
                        jo.Add(prop.Name, propVal != null ? JToken.FromObject(propVal, serializer) : null);
                    }
                }
            }
    
            jo.WriteTo(writer);
        }
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
            JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    
        public override bool CanRead
        {
            get { return false; }
        }
    }
    

    现在剩下的就是将属性添加到汽车类:

    public class Car
    {
        public string CarBrand { get; set; }
    
        [JsonConverter(typeof(SteeringWheelJsonConverter))]
        public ISteeringWheel SteeringWheel { get; set; }
    
    }
    

    【讨论】:

    • 能否请您给我看一个示例,因为我似乎无法使用 JsonConverter 属性更改 JSON 的“左侧”部分。意思是,我仍然有“标识符”:“...”
    • 感谢您的帮助!必须重新定义父级的转换器非常烦人,特别是因为我的代码中的 ISteeringWheelIdentifier 等效项实际上在几个类中使用,我必须以相同的方式重新定义它们(可能使用相同的代码,但仍然)。
    猜你喜欢
    • 2012-05-03
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-28
    • 1970-01-01
    • 2013-03-27
    相关资源
    最近更新 更多