WebAPI 序列化器序列化基类而不是笨拙的子类

问题描述 投票:0回答:3

我需要的类被序列化为 Web 服务响应主体。

问题是,基类的属性随之被序列化,而我无法为该服务提供该属性。

我需要阻止这些属性仅在这个子类上序列化。因此,我尝试使用

new
隐藏属性,但基类属性仍在序列化(即“Hello, world”位于生成的 http 响应正文中):

public class MyBaseClass
{
    public string MyProperty { get { return "Hello, world"; } }
}

public class MyChildClass : MyBaseClass
{
    [XmlIgnore]
    [JsonIgnore]
    public new string MyProperty { get; set; }
}

这通过这样的方式返回:

return myHttpRequestMessage.CreateResponse(myStatusCode, myChildClassInstance);

所以有两个问题

  • 那是怎么回事?为什么不用装饰品来表彰儿童班级?
  • 是否有另一种方法可以实现我想要实现的目标(这会阻止装饰属性被序列化?

我知道这完全是一个混杂,但在我有时间解决更深层次的问题(这是强制继承的操作)之前,这就是我必须处理的问题。

c# serialization asp.net-web-api json.net datacontractserializer
3个回答
1
投票

为了忽略基类中的属性,您可以在派生类中重写此属性并用 JsonIgnoreAttribute:

装饰它
public class MyBaseClass
{
    public virtual string MyProperty { get { return "Hello, world"; } }
}

public class MyChildClass : MyBaseClass
{
    [JsonIgnore]
    public override string MyProperty { get; }
}

你得到一个空的 json 对象序列化它:

Debug.Assert(Newtonsoft.Json.JsonConvert.SerializeObject(new MyChildClass()) == "{}");

0
投票

JSON

如果您使用 JSON.NET(这是 Web.API 中的默认 JSON 序列化程序),那么您可能需要使用自定义

ContractResolver
。这个问题的答案有一个创建此类类型的很好的例子。在下面的示例中,我将使用答案之一中的
IgnorableSerializerContractResolver

现在您可以在

Global.asax
注册:

var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new IgnorableSerializerContractResolver()
    .Ignore<MyBaseClass>(x => x.MyProperty);

如果您已经使用了一些合约解析器(例如

CamelCasePropertyNamesContractResolver
),那么您将需要以某种方式将它们组合起来。

XML

我不知道您使用什么类型(

DataContractSerializer
XmlSerializer
)的XML序列化,但据我所知,
DataContractSerializer
不允许在运行时排除属性。您将需要使用
XmlSerializer
。您可以为每种类型设置自定义序列化器:

var xmlOver = new XmlAttributeOverrides();
var xmlAttr = new XmlAttributes { XmlIgnore = true };
xmlOver.Add(typeof(MyBaseClass), "MyProperty", xmlAttr);

var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.SetSerializer<MyChildClass>(new XmlSerializer(typeof(MyChildClass), xmlOver));

简单方法

如果您有权访问您的

MyBaseClass
并允许进行一些更改,那么您可以使用
Data
属性解决您的问题:

[DataContract]
public class MyBaseClass
{
    public string MyProperty { get; set; }
}

[DataContract]
public class MyChildClass : MyBaseClass
{
    [DataMember]
    public string MyProperty2 { get; set; }
}

在这种情况下,您可以使用默认序列化器(JSON.NET 用于 JSON,

DataContractSerializer
用于 XML),它将自动为您解决。请注意,您必须
DataContract
添加到
MyBaseClass
,否则XML序列化将失败。


0
投票

我意识到其他人已经发布了类似的解决方案,但我想我也应该为这个问题提供更多背景信息。 您遇到的问题是您隐藏了基类的属性而不是覆盖它。

序列化器的工作方式是,它使用反射来查看对象,并查找属性和值,用于构建输出。

为了反射的目的,隐藏本质上创建了与原始属性同名的第二个属性。然而,原始属性对于反射仍然可见。 另一方面,重写本质上是重新定义基本属性。

.Net Fiddle 中的此代码示例显示了具有隐藏属性的类和具有重写属性的类之间的区别。您可以看到,在隐藏类反射中列出了这两个属性,但在重写的类中,它只列出了被重写属性的属性。

隐藏与覆盖

(大多数)序列化器是半智能的,如果您尝试序列化隐藏父类中的属性的子类,则序列化器应该仅序列化子类的属性。然而,在您的情况下会出现问题,因为尽管您告诉序列化器忽略子类中的属性,但反射已返回子属性和父属性。现在,序列化器可以自由地序列化父类中的属性(父类没有忽略属性)。

如果您重写该类,但是序列化程序只能访问子类及其属性,因此父类的忽略属性不会被序列化。

您需要做的第一件事是将您希望在基类上覆盖的属性设置为虚拟。这意味着如果需要的话可以覆盖它。像这样:

public class MyBaseClass
{
    public string MyProperty { get { return "Hello, world"; } }
}

完成此操作后,您现在可以覆盖子类中的属性并向其应用适当的属性。像这样:

public class MyChildClass : MyBaseClass
{
    [XmlIgnore]
    [JsonIgnore]
    public new string MyProperty { get; set; }
} 

通常在继承和序列化的范围内,如果我想更改其数据类型,我只会隐藏基类中的属性。

© www.soinside.com 2019 - 2024. All rights reserved.