【问题标题】:Deciding which properties are serialized at runtime决定在运行时序列化哪些属性
【发布时间】:2012-02-14 05:02:27
【问题描述】:

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

目前我能想到的“最简单”的方法是使用继承:

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 编写自己的序列化方法。

提前致谢 泰美克

==编辑

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

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 类非常简单,只有属性,便于维护和理解。

【问题讨论】:

  • 编写自定义序列化方法是我所知道的更好的方法(实际上并不难)。您有自定义序列化要求,这适用于自定义序列化程序......您是否有特殊原因不想走这条路?
  • 主要原因是我在框架环境中,序列化应该由框架完成,我只是将结果作为对象返回。
  • 你能看到这个环境中的其他类正在做什么来解决这个问题吗?如果您要将它破解成碎片,不妨像其他人一样破解它:)
  • 哈!我是第一个做“根据级别公开属性”的人。
  • 我想说是时候更新序列化框架了,因为有新的需求:)

标签: c# serialization


【解决方案1】:

这在很大程度上取决于您使用的序列化框架。您提到了 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 或任何给定序列化程序的等效项。

【讨论】:

  • 我将其标记为答案,因为它回答了问题,但我在问题本身中将我选择的方法描述为 ==Chosen 方法。
【解决方案2】:

您可以使用自定义属性装饰您的内部属性,指示它们应该被包含(或根据您的要求忽略),然后在您的 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 对 cme​​ts 的洞察。如果您只想在 levelLevel.Public 时序列化公共属性,您可以通过使用 BindingFlags.DeclaredOnly 标志反映类型的属性来实现该效果:

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

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

【讨论】:

  • 我认为这不能回答问题。他需要根据所需的级别序列化一组不同的属性,而不仅仅是在 public 时序列化。
  • @Bill - 问题在哪里说(或者当我回答时它说)? OP 发布的代码无法实现这一点,那么是什么让您认为这是他们想要的?除非我已经读懂了代码的意图。
  • @M 在他的代码中,他返回 CarInternal(所有属性都被序列化)或 CarPublic(只有公共属性被序列化)。请参阅他的 ToSerialize 方法,该方法基于 Level.Public 进行选择。
  • @Bill - 你可能是对的(我过度简化了情况,因为我不认为这是一种可能性)。我会相应地更新。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 2020-03-09
  • 1970-01-01
  • 1970-01-01
  • 2021-01-27
相关资源
最近更新 更多