如何将JSON反序列化为使用组合的C#类

如何将JSON反序列化为使用组合的C#类,c#,json,serialization,C#,Json,Serialization,我需要反序列化 {'Id':'id123', “时间”:143621503, 'Name':'foo', “ProductId”:1} 装入集装箱1 public class Container1 { public CommonFields Common { get; set; } //fields specific to Container1 [JsonProperty(PropertyName = "Name")] public string Name {

我需要反序列化

{'Id':'id123',
“时间”:143621503,
'Name':'foo',
“ProductId”:1}

装入集装箱1

public class Container1
{
    public CommonFields Common { get; set; }

    //fields specific to Container1

    [JsonProperty(PropertyName = "Name")]
    public string Name { get; set; }

    [JsonProperty(PropertyName = "ProductId")]
    public int ProductId { get; set; }
}

{
“Id”:“id123”,
“时间”:143621503,
“组”:“10768C21-9971-4D2F-ACD7-10C2EF19FCA8”
}

装入集装箱2

public class Container2
{
    public CommonFields Common { get; set; }

    //fields specific to Container2

    [JsonProperty(PropertyName = "Group")]
    public Guid Group { get; set; }
}
使用组合(而不是继承)。两个JSON都有两个公共字段(Id和时间)和特定字段

使用newtonsoft.json
JsonConvert.DeserializeObject(json\u container1)
结果是2容器的属性被正确反序列化。组合类的公共属性未反序列化

如何将JSON反序列化为只使用组合的C#类?

(不强制使用newtonsoft.json)

下面是我的尝试

public class CommonFields
{
    [JsonProperty(PropertyName = "Id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "Time")]
    public long Time { get; set; }
}

public class Container1
{
    public CommonFields Common { get; set; }
    [JsonProperty(PropertyName = "Name")]
    public string Name { get; set; }
    [JsonProperty(PropertyName = "ProductId")]
    public int ProductId { get; set; }
}

public class Container2
{
    public CommonFields Common { get; set; }
    [JsonProperty(PropertyName = "Group")]
    public Guid Group { get; set; }
}

internal class Program
{
    private static void Main(string[] args)
    {
        string json_container1 = @"{
'Id': 'id123',
'Time': 1436231503,
'Name': 'foo',
'ProductId': 1
 }";

        string json_container2 = @"{
'Id': 'id123',
'Time': 1436231503,
'Group':'10768C21-9971-4D2F-ACD7-10C2EF19FCA8'
 }";

        var container1Obj = JsonConvert.DeserializeObject<Container1>(json_container1); 

        var container2Obj = JsonConvert.DeserializeObject<Container2>(json_container2);

        Console.ReadKey();
}}}
公共类公共字段
{
[JsonProperty(PropertyName=“Id”)]
公共字符串Id{get;set;}
[JsonProperty(PropertyName=“Time”)]
公共长时间{get;set;}
}
公共类集装箱1
{
公共公共字段公共{get;set;}
[JsonProperty(PropertyName=“Name”)]
公共字符串名称{get;set;}
[JsonProperty(PropertyName=“ProductId”)]
public int ProductId{get;set;}
}
公共类集装箱2
{
公共公共字段公共{get;set;}
[JsonProperty(PropertyName=“Group”)]
公共Guid组{get;set;}
}
内部课程计划
{
私有静态void Main(字符串[]args)
{
字符串json_container1=@”{
“Id”:“id123”,
“时间”:143621503,
'Name':'foo',
“ProductId”:1
}";
字符串json_container2=@”{
“Id”:“id123”,
“时间”:143621503,
“组”:“10768C21-9971-4D2F-ACD7-10C2EF19FCA8”
}";
var container1Obj=JsonConvert.DeserializeObject(json_container1);
var container2Obj=JsonConvert.DeserializeObject(json_container2);
Console.ReadKey();
}}}

不要这样做。

从中反序列化的JSON元素不应更改,您可以删除一些属性,但更改其属性结构是一种不好的做法


JSON file\content应该有一个兼容的JSON类,如果您想进行任何更改,请创建另一个自定义类,并在它们之间创建映射逻辑。

我认为您可以仅在两个对象的公共字段上再次进行反序列化

    container1Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container1);
    container2Obj.Common = JsonConvert.DeserializeObject<CommonFields>(json_container2);
container1Obj.Common=JsonConvert.DeserializeObject(json_container1);
container2Obj.Common=JsonConvert.DeserializeObject(json_container2);

我知道你想使用组合,但我真的看不出在这里使用组合而不是继承有什么好处

public class BaseClass
{
    [JsonProperty(PropertyName = "Id")]
    public string Id { get; set; }

    [JsonProperty(PropertyName = "Time")]
    public long Time { get; set; }
}

public class Container1 : BaseClass
{
    [JsonProperty(PropertyName = "Name")]
    public string Name { get; set; }

    [JsonProperty(PropertyName = "ProductId")]
    public int ProductId { get; set; }
}

public class Container2 : BaseClass
{
    [JsonProperty(PropertyName = "Group")]
    public Guid Group { get; set; }
}

这很简单,应该能完成任务。

你的问题基本上与问题相反,可以用类似的策略解决。为了简化操作,请为包含公共字段的所有类创建一个接口:

public interface IHasCommonFields
{
    CommonFields Common { get; set; }
}
然后,您可以为实现此接口的任何类型创建以下通用转换器:

public class HasCommonFieldsConverter<T> : JsonConverter where T : IHasCommonFields
{
    [ThreadStatic]
    static bool disabled;

    // Disables the converter in a thread-safe manner.
    bool Disabled { get { return disabled; } set { disabled = value; } }

    public override bool CanWrite { get { return !Disabled; } }

    public override bool CanRead { get { return !Disabled; } }

    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        if (token == null || token.Type == JTokenType.Null)
            return null;
        using (new PushValue<bool>(true, () => Disabled, val => Disabled = val)) // Prevent infinite recursion of converters
        {
            var hasCommon = token.ToObject<T>(serializer);
            var common = (hasCommon.Common ?? (hasCommon.Common = new CommonFields()));
            serializer.Populate(token.CreateReader(), common);
            return hasCommon;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
            return;
        }
        using (new PushValue<bool>(true, () => Disabled, val => Disabled = val))  // Prevent infinite recursion of converters
        {
            var hasCommon = (T)value;
            var obj = JObject.FromObject(hasCommon, serializer);
            var common = hasCommon.Common;
            if (common != null)
            {
                var commonObj = JObject.FromObject(common, serializer);
                obj.Merge(commonObj);
            }
            obj.WriteTo(writer);
        }
    }
}

public struct PushValue<T> : IDisposable
{
    Action<T> setValue;
    T oldValue;

    public PushValue(T value, Func<T> getValue, Action<T> setValue)
    {
        if (getValue == null || setValue == null)
            throw new ArgumentNullException();
        this.setValue = setValue;
        this.oldValue = getValue();
        setValue(value);
    }

    #region IDisposable Members

    // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
    public void Dispose()
    {
        if (setValue != null)
            setValue(oldValue);
    }

    #endregion
}
然后,要测试:

public class TestClass
{
    public static void Test()
    {
        var container1 = new Container1 { Name = "name", ProductId = 101, Common = new CommonFields { Id = "1401", Time = DateTime.Today.Ticks } };
        var container2 = new Container2 { Group = Guid.NewGuid(), Common = new CommonFields { Id = "2401", Time = DateTime.Today.Ticks } };

        Test(container1);
        Test(container2);
    }

    private static void Test<T>(T container) where T : class, IHasCommonFields
    {
        var json = JsonConvert.SerializeObject(container, Formatting.Indented);
        Debug.WriteLine(json);
        var containerback = JsonConvert.DeserializeObject<T>(json);
        var json2 = JsonConvert.SerializeObject(containerback, Formatting.Indented);
        Debug.Assert(json == json2); // No assert
        if (container.Common != null)
        {
            Debug.Assert(container.Common.Id == containerback.Common.Id); // No assert
            Debug.Assert(container.Common.Time == containerback.Common.Time); // No assert
        }
    }
}

此转换器的一个缺点是,如果序列化时
Common
属性为null,则将使用默认值反序列化为非null

如果您不想使用
IHasCommonFields
接口,例如,您可以使用抽象方法为转换器创建一个抽象泛型基类,以获取和设置公共字段,然后在每个子类中重写这些方法


(老实说,这里的继承似乎比组合更简单,正如其他答案所述。)

为什么要首先避免使用继承?您可以有一个包含公共字段的基类和从中继承的两个子类。
public class TestClass
{
    public static void Test()
    {
        var container1 = new Container1 { Name = "name", ProductId = 101, Common = new CommonFields { Id = "1401", Time = DateTime.Today.Ticks } };
        var container2 = new Container2 { Group = Guid.NewGuid(), Common = new CommonFields { Id = "2401", Time = DateTime.Today.Ticks } };

        Test(container1);
        Test(container2);
    }

    private static void Test<T>(T container) where T : class, IHasCommonFields
    {
        var json = JsonConvert.SerializeObject(container, Formatting.Indented);
        Debug.WriteLine(json);
        var containerback = JsonConvert.DeserializeObject<T>(json);
        var json2 = JsonConvert.SerializeObject(containerback, Formatting.Indented);
        Debug.Assert(json == json2); // No assert
        if (container.Common != null)
        {
            Debug.Assert(container.Common.Id == containerback.Common.Id); // No assert
            Debug.Assert(container.Common.Time == containerback.Common.Time); // No assert
        }
    }
}
{
  "Name": "name",
  "ProductId": 101,
  "Id": "1401",
  "Time": 635725152000000000
}
{
  "Group": "9ed31118-c0b7-4d9f-8f57-303b2e164643",
  "Id": "2401",
  "Time": 635725152000000000
}