决定在运行时序列化哪些属性

本文关键字:属性 序列化 运行时 决定 | 更新日期: 2023-09-27 18:24:18

假设我必须在Internal和Public等级别中序列化Car类的对象。公共级别中的某些属性不应序列化,因为它们是内部的。

目前,我能想到的实现这一目标的"最简单"的方法是使用继承:

class CarPublic {
  public int PropX {get;set}
}
class CarInternal: CarPublic {
  public string PropY {get;set}
}

然后我可以

object ToSerialize() {
 CarInternal car = GetCar();
 if( level == Level.Public ) { 
    return car as CarPublic;
 } else {
     return car;
 }
}

ToSerialize()的结果由一个框架获取(我没有控制),并序列化为JSON或XML。

为了简单起见,我省略了XML序列化属性。

这感觉像是一次黑客攻击,而黑客攻击只会让你走这么远。有更好的方法吗?

我认为现在已经很清楚了,但我希望避免为JSON和XML编写自己的序列化方法。

提前感谢Tymek

==编辑

为了澄清,我希望能够序列化多个级别:

class Car0 {
  public int PropA {get;set}
}
class Car1: Car0 {
  public string PropB {get;set}
}
class Car2: Car1 {
  public int PropC {get;set}
}
class Car3: Car2 {
  public string PropD {get;set}
}

object ToSerialize( Level level ) {
 Car3 car = GetCar();
 switch( level ) {
   case Level.Zero: return car as Car0;
   case Level.One: return car as Car1;
   case Level.Two: return car as Car3;
   case Level.Three: return car as Car4;
 }
 return null;
}

==选择的进近

我将Marc Gravell的答案标记为答案,因为它提供了C#及其"标准"组件如何支持我所要求的内容的一般信息。

然而,我认为解决我的问题的最佳方法是使用如上所示的代理类使用如下所示的方法,以这种多级模式序列化类。

public interface ICar {
    Car0 As0();
    Car1 As1();
    Car2 As2();
    Car3 As3();
 ...
 }

这允许保持Car0..3类非常简单,只有属性,以便维护和理解。

决定在运行时序列化哪些属性

这在很大程度上取决于您使用的序列化框架。您提到了xml和json,首先要注意的是,您可以使用进行装饰

[XmlIgnore]
public int PropX {get;set;}

[ScriptIgnore]
public int PropX {get;set;}

XmlSerializerJavascriptSerializer将响应哪个。如果您需要根据每个实例做出决定,则有ShouldSerialize**Specified模式:

public bool ShouldSerializePropX() {
   // return true to serialize, false to omit
}

以上是XmlSerializer和其他人使用的基于名称的模式;它有一个双胞胎:

[XmlIgnore, Browsable(false)]
public bool PropXSpecified {
    get { /* return true to serialize, false to omit */ }
    set { /* can just drop this value - don't need to assign */ }
}

你不需要做任何事情来连接它们——它们会自动工作。

不同的序列化程序允许不同的模式。

此外,有时您可以在运行时添加[XmlIgnore]之类的内容,例如通过XmlAttributeOverrides,或任何给定序列化程序的等效内容。

您可以用一个自定义属性来装饰您的内部属性,指示它们应该被包括(或根据您的要求被忽略),然后在ToSerialize中检查该属性。

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ShouldSerializeAttribute : Attribute { }

然后你得到的类定义看起来像:

class Car 
{
    [ShouldSerialize]
    public int PropX {get;set}
    // This property won't be serialized because it is internal
    public int PropY { get; set; }
}

你的ToSerialize看起来像:

object ToSerialize() 
{
    Car car = GetCar();
    foreach(PropertyInfo propInfo in car.GetType().GetProperties())
    {
        if(ShouldSerialize(propInfo)) 
        { 
            return car;
        }
    }
}

ShouldSerialize的样子:

internal bool ShouldSerialize(PropertyInfo propInfo)
{
    return propInfo.GetCustomAttributes(typeof(ShouldSerializeAttribute), true).FirstOrDefault() != null;
}

更新

基于@Bill在评论中的见解。如果您希望在levelLevel.Public时仅序列化公共属性,则可以通过使用BindingFlags.DeclaredOnly标志反映类型的属性来实现此效果:

foreach(PropertyInfo propInfo in car.GetType().GetProperties(BindingFlags.DeclaredOnly))

这应该返回一个仅由car的当前实例声明的属性列表。