使用DataContractJsonSerializer,将JSON字符串反序列化为C#对象,该对象具有list&;作为属性的接口

使用DataContractJsonSerializer,将JSON字符串反序列化为C#对象,该对象具有list&;作为属性的接口,c#,json,rest,serialization,datacontractjsonserializer,C#,Json,Rest,Serialization,Datacontractjsonserializer,我正在从事一个调用第三方REST服务的c#.dotNet项目 示例类结构: [Serializable] [DataContract(Name = "MainClass")] [KnownType(typeof(Class1))] [KnownType(typeof(Class2))] public class MainClass : IMainInterface { public MainClass() { Value2 = new List<IClas

我正在从事一个调用第三方REST服务的c#.dotNet项目

示例类结构:

[Serializable]
[DataContract(Name = "MainClass")]
[KnownType(typeof(Class1))]
[KnownType(typeof(Class2))]
public class MainClass : IMainInterface
{

    public MainClass()
    {
        Value2 = new List<IClass2>();
    }

    [DataMember(Name = "class")]
    public IClass1 Value1 { get; set; }

    [DataMember(Name = "classes")]
    public List<IClass2> Value2 { get; set; }

}

[Serializable]
[Export(typeof(IClass1))]
[ExportMetadata("IClass1", "Class1")]
[DataContract(Name = "class1")]
public class Class1 : IClass1
{
    [ImportingConstructor]
    public Class1()
    {

    }

    [DataMember(Name = "prop1")]
    public string Prop1 { get; set; }

    [DataMember(Name = "prop2")]
    public string Prop2 { get; set; }

    [DataMember(Name = "prop3")]
    public string Prop3 { get; set; }

}

[Serializable]
[Export(typeof(IClass2))]
[ExportMetadata("IClass2", "Class2")]
[DataContract]
public class Class2 : IClass2
{
    [ImportingConstructor]
    public Class2()
    { }


    [DataMember(Name = "propA")]
    public string PropA { get; set; }

    [DataMember(Name = "propB")]
    public string PropB { get; set; }

    [DataMember(Name = "propC")]
    public string PropC { get; set; }
}

public interface IMainInterface
{
    IClass1 Value1 { get; set; }

    List<IClass2> Value2 { get; set; }
}

public interface IClass1
{
    string Prop1 { get; set; }

    string Prop2 { get; set; }

    string Prop3 { get; set; }
}

public interface IClass2
{
    string PropA { get; set; }

    string PropB { get; set; }

    string PropC { get; set; }
}
Json_String2:(无类型提示)

因此,对于给定的类结构,如果我使用
DataContractJsonSerializer
生成json(属于
MainClass
的对象),我得到的是json\u String1,如果我直接反序列化,它工作得很好

而当GETting数据时,响应是Json\u String2(w/o类型提示)。因此,在反序列化时,我得到以下错误

InvalidCastException未处理。无法强制转换类型为的对象 “System.Object”以键入“WpfApplication1.IClass2”

现在,我必须通过添加类型提示手动修改上面的json(字符串操作),才能成功地对其进行反序列化

问题1)如何避免这种Json字符串反序列化操作?

问题2)如何在没有类型提示的情况下创建json?

编辑: 1.添加了由MainClass实现的IMainInterface
2.dotnetframework4

问题在于您试图反序列化到一个只有接口的类。当JSON指定_类型时,它显然可以工作。但是,如果没有(如第二个JSON示例中所示),ReadObject将无法自动解析其实现的接口

尝试使用
MainClass
中的具体类
Class1
Class2
,而不是它们的接口(IClass1、IClass2)。代码的其余部分可以保持原样。这两个Json示例都可以很好地处理这个问题

[Serializable]
[DataContract(Name = "MainClass")]
[KnownType(typeof(Class1))]
[KnownType(typeof(Class2))]
public class MainClass
{

    public MainClass()
    {
        Value2 = new List<Class2>();
    }

    [DataMember(Name = "class")]
    public Class1 Value1 { get; set; }

    [DataMember(Name = "classes")]
    public List<Class2> Value2 { get; set; }

}
[可序列化]
[DataContract(Name=“MainClass”)]
[KnownType(typeof(Class1))]
[知识类型(类别2))]
公共类主类
{
公共类()
{
Value2=新列表();
}
[DataMember(Name=“class”)]
公共类1值1{get;set;}
[DataMember(Name=“classes”)]
公共列表值2{get;set;}
}

由于您的类实际上都不是多态的,因此有两种解决方案可以使用.Net内置类库:

解决方案1:
JavaScriptSerializer
Solution

通过使用。但是,它不允许重新映射字段名和属性名,因此属性名必须与要处理的JSON中的名称匹配。以下转换器实现了这一功能:

public class InterfaceToClassConverter<TInterface, TClass> : JavaScriptConverter where TClass : class, TInterface
{
    public InterfaceToClassConverter()
    {
        if (typeof(TInterface) == typeof(TClass))
            throw new ArgumentException(string.Format("{0} and {1} must not be the same type", typeof(TInterface).FullName, typeof(TClass).FullName)); // Or else you would get infinite recursion!
        if (!typeof(TInterface).IsInterface)
            throw new ArgumentException(string.Format("{0} must be an interface", typeof(TInterface).FullName));
        if (typeof(TClass).IsInterface)
            throw new ArgumentException(string.Format("{0} must be a class not an interface", typeof(TClass).FullName));
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (type == typeof(TInterface))
            return serializer.ConvertToType<TClass>(dictionary);
        return null;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            // For an interface-valued property such as "IFoo  Foo { getl set; },
            // When serializing, JavaScriptSerializer knows the actual concrete type being serialized -- which is never an interface.
            // When deserializing, JavaScriptSerializer only knows the expected type, which is an interface.  Thus by returning
            // only typeof(TInterface), we ensure this converter will only be called during deserialization, not serialization.
            return new[] { typeof(TInterface) };
        }
    }
}

请注意,如果调用者试图设置不是实际的
Class1
Class2
IClass1
Class2
,则将抛出
InvalidCastException
。因此,这会使界面实现看起来隐藏起来,而不会真正保持实现的私有性。

在这种情况下,如何调用ParseResponse?不能将其称为ParseResponse()?@Icepickle
ParseResponse(jsonResponse),其中jsonResponse是来自GETAPI调用的响应。定义了Json结构,因此解析Class2不会给出所需的输出。那么在main类中,您可以将
列表
设置为
列表
否?作为基类?我的意思是,在反序列化过程中,它必须实例化变量,而接口是无法实现的instantiate@Icepickle该类通过接口向其他应用程序公开,并使用IoC的原理在用户端创建obj。因此,我现在无法更改类结构,因此发布了这个问题,以了解是否有任何方法可以实现这一点。您必须使用
DataContractJsonSerializer
,还是可以切换到
JavaScriptSerializer
或Json.NET?问题是如何在类中保留“接口”引用。这是可行的,但我只想用接口Ref来构造我的类。谢谢你的发帖,但这对我没有帮助。布拉玛,我不知道你也受到了班级结构的限制。接口的问题(我相信你已经知道了)是。。。当没有指定类型时,反序列化程序无法确定要使用哪个实现(可能有很多实现)。这非常有用。虽然我不得不修改一些东西来适应我的项目,但它确实奏效了。
[Serializable]
[DataContract(Name = "MainClass")]
[KnownType(typeof(Class1))]
[KnownType(typeof(Class2))]
public class MainClass
{

    public MainClass()
    {
        Value2 = new List<Class2>();
    }

    [DataMember(Name = "class")]
    public Class1 Value1 { get; set; }

    [DataMember(Name = "classes")]
    public List<Class2> Value2 { get; set; }

}
public class InterfaceToClassConverter<TInterface, TClass> : JavaScriptConverter where TClass : class, TInterface
{
    public InterfaceToClassConverter()
    {
        if (typeof(TInterface) == typeof(TClass))
            throw new ArgumentException(string.Format("{0} and {1} must not be the same type", typeof(TInterface).FullName, typeof(TClass).FullName)); // Or else you would get infinite recursion!
        if (!typeof(TInterface).IsInterface)
            throw new ArgumentException(string.Format("{0} must be an interface", typeof(TInterface).FullName));
        if (typeof(TClass).IsInterface)
            throw new ArgumentException(string.Format("{0} must be a class not an interface", typeof(TClass).FullName));
    }

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        if (type == typeof(TInterface))
            return serializer.ConvertToType<TClass>(dictionary);
        return null;
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            // For an interface-valued property such as "IFoo  Foo { getl set; },
            // When serializing, JavaScriptSerializer knows the actual concrete type being serialized -- which is never an interface.
            // When deserializing, JavaScriptSerializer only knows the expected type, which is an interface.  Thus by returning
            // only typeof(TInterface), we ensure this converter will only be called during deserialization, not serialization.
            return new[] { typeof(TInterface) };
        }
    }
}
public interface IMainInterface
{
    IClass1 @class { get; set; } // NOTICE ALL PROPERTIES WERE RENAMED TO MATCH THE JSON NAMES.
    List<IClass2> classes { get; set; }
}

[Serializable]
[DataContract(Name = "MainClass")]
[KnownType(typeof(Class1))]
[KnownType(typeof(Class2))]
public class MainClass : IMainInterface
{
    public MainClass()
    {
        classes = new List<IClass2>();
    }

    [DataMember(Name = "class")]
    public IClass1 @class { get; set; }

    [DataMember(Name = "classes")]
    public List<IClass2> classes { get; set; }
}

[Serializable]
[DataContract(Name = "class1")]
public class Class1 : IClass1
{
    public Class1() {}

    [DataMember(Name = "prop1")]
    public string prop1 { get; set; }

    [DataMember(Name = "prop2")]
    public string prop2 { get; set; }

    [DataMember(Name = "prop3")]
    public string prop3 { get; set; }

}

[Serializable]
[DataContract]
public class Class2 : IClass2
{
    public Class2() { }

    [DataMember(Name = "propA")]
    public string propA { get; set; }

    [DataMember(Name = "propB")]
    public string propB { get; set; }

    [DataMember(Name = "propC")]
    public string propC { get; set; }
}

public interface IClass1
{
    string prop1 { get; set; }
    string prop2 { get; set; }
    string prop3 { get; set; }
}

public interface IClass2
{
    string propA { get; set; }
    string propB { get; set; }
    string propC { get; set; }
}

public static class TestJavaScriptConverter
{
    public static void Test()
    {
        string json = @"
            {
            ""class"":
                {""prop1"":""TestVal0"",""prop2"":""TestVal2"",""prop3"":""TestVal3""},
            ""classes"":
                [
                    {""propA"":""A"",""propB"":""B"",""propC"":""C""},
                    {""propA"":""X"",""propB"":""Y"",""propC"":""Z""},
                    {""propA"":""1"",""propB"":""2"",""propC"":""3""}
                ]
            }";

        var serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new JavaScriptConverter[] { new InterfaceToClassConverter<IClass1, Class1>(), new InterfaceToClassConverter<IClass2, Class2>() });
        var main1 = serializer.Deserialize<MainClass>(json);
        var json2 = serializer.Serialize(main1);
        Debug.WriteLine(json2);
        var main2 = serializer.Deserialize<MainClass>(json2);

        Debug.Assert(main1.@class.ToStringWithReflection() == main2.@class.ToStringWithReflection()); // No assert
        Debug.Assert(main1.classes.Select(c => c.ToStringWithReflection()).SequenceEqual(main2.classes.Select(c => c.ToStringWithReflection()))); // no assert
    }
}
public interface IMainInterface
{
    IClass1 Value1 { get; set; }
    IList<IClass2> Value2 { get; set; }
}

[Serializable]
[DataContract(Name = "MainClass")]
public class MainClass : IMainInterface
{
    [DataMember(Name = "class")]
    Class1 RealValue1 { get; set; }

    [DataMember(Name = "classes")]
    private List<Class2> RealList2 { get; set; }

    IList<IClass2> list2Proxy; // can't be readonly because the DataContactJsonSerializer does not call the default constructor.

    private IList<IClass2> List2Proxy
    {
        get
        {
            if (list2Proxy == null)
                Interlocked.CompareExchange(ref list2Proxy, new ConvertingList<Class2, IClass2>(() => this.RealList2, c => c, ToClass), null);
            return list2Proxy;
        }
    }

    Class2 ToClass(IClass2 iClass)
    {
        // REWRITE TO FIT YOUR NEEDS
        return (Class2)iClass;
    }

    Class1 ToClass(IClass1 iClass)
    {
        // REWRITE TO FIT YOUR NEEDS
        return (Class1)iClass;
    }

    public MainClass()
    {
        RealList2 = new List<Class2>();
    }

    [IgnoreDataMember]
    public IClass1 Value1
    {
        get
        {
            return RealValue1;
        }
        set
        {
            RealValue1 = ToClass(value);
        }
    }

    [IgnoreDataMember]
    public IList<IClass2> Value2
    {
        get
        {
            return List2Proxy;
        }
        set
        {
            if (value == null)
            {
                RealList2.Clear();
                return;
            }
            if (List2Proxy == value)
                return;
            RealList2 = value.Select<IClass2, Class2>(ToClass).ToList();
        }
    }
}

public class ConvertingList<TIn, TOut> : IList<TOut>
{
    readonly Func<IList<TIn>> getList;
    readonly Func<TIn, TOut> toOuter;
    readonly Func<TOut, TIn> toInner;

    public ConvertingList(Func<IList<TIn>> getList, Func<TIn, TOut> toOuter, Func<TOut, TIn> toInner)
    {
        if (getList == null || toOuter == null || toInner == null)
            throw new ArgumentNullException();
        this.getList = getList;
        this.toOuter = toOuter;
        this.toInner = toInner;
    }

    IList<TIn> List { get { return getList(); } }

    TIn ToInner(TOut outer) { return toInner(outer); }

    TOut ToOuter(TIn inner) { return toOuter(inner); }

    #region IList<TOut> Members

    public int IndexOf(TOut item)
    {
        return List.IndexOf(toInner(item));
    }

    public void Insert(int index, TOut item)
    {
        List.Insert(index, ToInner(item));
    }

    public void RemoveAt(int index)
    {
        List.RemoveAt(index);
    }

    public TOut this[int index]
    {
        get
        {
            return ToOuter(List[index]);
        }
        set
        {
            List[index] = ToInner(value);
        }
    }

    #endregion

    #region ICollection<TOut> Members

    public void Add(TOut item)
    {
        List.Add(ToInner(item));
    }

    public void Clear()
    {
        List.Clear();
    }

    public bool Contains(TOut item)
    {
        return List.Contains(ToInner(item));
    }

    public void CopyTo(TOut[] array, int arrayIndex)
    {
        foreach (var item in this)
            array[arrayIndex++] = item;
    }

    public int Count
    {
        get { return List.Count; }
    }

    public bool IsReadOnly
    {
        get { return List.IsReadOnly; }
    }

    public bool Remove(TOut item)
    {
        return List.Remove(ToInner(item));
    }

    #endregion

    #region IEnumerable<TOut> Members

    public IEnumerator<TOut> GetEnumerator()
    {
        foreach (var item in List)
            yield return ToOuter(item);
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    #endregion
}