【问题标题】:Most elegant XML serialization of Color structureColor 结构最优雅的 XML 序列化
【发布时间】:2011-03-17 20:37:34
【问题描述】:

一个问题困扰着我,足以在 Stack Overflow 上注册。目前,如果我想将 Color 序列化为 XML 字符串作为命名颜色,或#rrggbb,或#aarrggbb,我这样做:

[XmlIgnore()]
public Color color;

[XmlElement(ElementName = "Color")]
public String color_XmlSurrogate
{
  get { return MyColorConverter.SetColor(color); }
  set { color = MyColorConverter.GetColor(value); }
}

MyColorConverter 以我喜欢的方式进行序列化。但这一切感觉就像一个杂物,还有额外的领域和所有东西。有没有办法让它在更少的行中工作,也许将 TypeDescriptor 与与 XML 相关的 C# 属性连接起来?

【问题讨论】:

  • 我认为您已经达到了 XML 序列化的极限。您可能想查看 DataContract 序列化,

标签: c# xml-serialization


【解决方案1】:

很痛苦,不是吗?这就是你可以用XmlSerializer 做的所有事情,除非你实现IXmlSerializable(我确实推荐)。选项:

  • 坚持这一点,但也将 color_XmlSurrogate 标记为 [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] - 这将阻止它出现在大多数数据绑定视图中,以及在将程序集引用为 dll 时出现在代码编辑器中
  • 使用DataContractSerializer,它支持私有属性(但支持xml属性;你赢不了...)

顺便说一句,我会将color 作为属性,而不是字段:

[XmlIgnore]
public Color Color {get;set;}

【讨论】:

    【解决方案2】:

    这是我用来序列化 XML 中的 Color 结构的东西。在我看来,这比隐藏主要的Color 属性要好。欢迎提出任何建议。

    XmlColor 类主要依靠implicit operator 语言功能来提供关键数据转换。没有这个,这个类基本上是没用的。添加了其他一些功能来完善课程。

    XmlColor 助手还提供了一种方便的方法来分离颜色分量。我添加了 Alpha 属性来显示这一点。请注意,如果Alpha 组件一直上升到 255,则不会被序列化。

    反序列化Web 颜色值会结合当前存储在实例中的Alpha 值。解析属性的顺序无关紧要。如果 XML 源中缺少 Alpha 属性,则实例组件值将用于设置 Alpha 级别。这可以说是错误的;但是,在 XML 序列化的情况下,XmlColor 类将使用 Color.Black 初始化,将 Alpha 设置为 255。

    我正在使用 VS2010 环境并针对 .Net 4 进行构建。我不知道代码与以前版本的兼容性如何。

    这是一个应序列化为 XML 的示例属性:

        [XmlElement(Type=typeof(XmlColor))]
        public Color MyColor { get; set; }
    

    这里是 XmlColor 助手类:

    public class XmlColor
    {
        private Color color_ = Color.Black;
    
        public XmlColor() {}
        public XmlColor(Color c) { color_ = c; }
    
    
        public Color ToColor()
        {
            return color_;
        }
    
        public void FromColor(Color c)
        {
            color_ = c;
        }
    
        public static implicit operator Color(XmlColor x)
        {
            return x.ToColor();
        }
    
        public static implicit operator XmlColor(Color c)
        {
            return new XmlColor(c);
        }
    
        [XmlAttribute]
        public string Web
        {
            get { return ColorTranslator.ToHtml(color_); }
            set {
                try
                {
                    if (Alpha == 0xFF) // preserve named color value if possible
                        color_ = ColorTranslator.FromHtml(value);
                    else
                        color_ = Color.FromArgb(Alpha, ColorTranslator.FromHtml(value));
                }
                catch(Exception)
                {
                    color_ = Color.Black;
                }
            }
        }
    
        [XmlAttribute]
        public byte Alpha
        {
            get { return color_.A; }
            set { 
                if (value != color_.A) // avoid hammering named color if no alpha change
                    color_ = Color.FromArgb(value, color_); 
            }
        }
    
        public bool ShouldSerializeAlpha() { return Alpha < 0xFF; }
    }
    

    【讨论】:

    • 我喜欢这个!如果它有效,那就是。我将 VS2008 与 .NET 3.5 一起使用,所以我需要在将来的某个时间检查一下。是否有理由使用 ToColor/FromColor 而不是某些 Color 属性?可以将其重新编码为只有一个 xml 字符串作为序列化值吗?我希望这是结果:&lt;Color&gt;#aarrggbb&lt;/Color&gt;
    • 您可以将类剥离为隐式运算符和单个可发布属性。该属性如何序列化完全取决于您。我注意到无论 alpha 值如何,调用 Argb 方法都会产生十六进制字符串。我选择 XmlAttribute 是为了避免使用子元素。您可以考虑使用“ARGB”XmlAttribute 并通过使用 Argb 方法强制 4 个组件十六进制字符串。毫无疑问,如果您觉得有帮助,您可以挤入 Color 属性。
    • @bvj,可惜我不能给你一个赏金:)
    • 这是天才。现在我只需要在我的代码上添加一个[XmlElement(Type=typeof(XmlColor))] 属性。确实是最优雅的解决方案。
    • 如果您正在使用 WPF 颜色(或将您的 WinForms 颜色转换为 WPF),自动序列化将起作用,但它有点难看(给您一个 A、R、G 的 XML 元素、B、ScA、ScR、ScG 和 ScB 各)。但是使用 bvj 解决方案的略微修改版本,您只需将 ColorTranslator.ToHtml() 替换为 color_.ToString() 并将 ColorTranslator.FromHtml() 替换为 ColorConverter.FromString() 即可获得更好的输出。结果将以“#FF000000”的形式存储,因此您甚至不需要处理单独的Alpha值。
    【解决方案3】:

    我找到了另一个解决方案,

    可以使用 System.Windows.Media.Color 代替 System.Drawing.Color
    它可以序列化为 XML。

    【讨论】:

    • 我试过了,对于一个颜色值,我得到了几个我无法控制的标签。这不是我想要的序列化。
    • @Dialectus,请解释一下。
    • 对于颜色 (A=123, R=45, G=67, B=89),这是序列化的结果:&lt;Color&gt;&lt;A&gt;123&lt;/A&gt;&lt;R&gt;45&lt;/R&gt;&lt;G&gt;67&lt;/G&gt;&lt;B&gt;89&lt;/B&gt;&lt;ScA&gt;0.482352942&lt;/ScA&gt;&lt;ScR&gt;0.0262412224&lt;/ScR&gt;&lt;ScG&gt;0.0561284944&lt;/ScG&gt;&lt;ScB&gt;0.09989873&lt;/ScB&gt;&lt;/Color&gt;。我想要的结果是&lt;Color&gt;#7B2D4359&lt;/Color&gt;。此外,我真的需要 GDI 颜色,而不是媒体颜色,因为这是我正在使用的结构。
    • @Dialectus,好的,我明白你的意思。但出于某些目的,它可能没问题。
    【解决方案4】:

    由于这是我在这里的第一个问题,因此我决定对其进行更多调查。 @bvj 给出了一个很好的答案。我调整了他的代码,所以就在这里。 XmlColor 类已更改,以便序列化的字符串进入标签的文本,如果字符串应序列化为 XML 属性而不是标签,则有公共静态函数。不幸的是,属性仍然必须使用代理字段进行序列化,但如果我错了,请纠正我。使用类:

    // Color as tag
    [XmlElement(Type = typeof(XmlColor))]
    public Color ColorAsTag { get; set; }
    
    // Color as attribute
    [XmlIgnore]
    public Color ColorAsAttribute { get; set; }
    
    [XmlAttribute("ColorAsAttribute")]
    public string ColorAsAttribute_XmlSurrogate
    {
        get { return XmlColor.FromColor(ColorAsAttribute); }
        set { ColorAsAttribute = XmlColor.ToColor(value); }
    }
    

    实现它的类:

    public class XmlColor
    {
        private Color color_ = Color.Black;
    
        public XmlColor() { }
        public XmlColor(Color c) { color_ = c; }
    
    
        public static implicit operator Color(XmlColor x)
        {
            return x.color_;
        }
    
        public static implicit operator XmlColor(Color c)
        {
            return new XmlColor(c);
        }
    
        public static string FromColor(Color color)
        {
            if (color.IsNamedColor)
                return color.Name;
    
            int colorValue = color.ToArgb();
    
            if (((uint)colorValue >> 24) == 0xFF)
                return String.Format("#{0:X6}", colorValue & 0x00FFFFFF);
            else
                return String.Format("#{0:X8}", colorValue);
        }
    
        public static Color ToColor(string value)
        {
            try
            {
                if (value[0] == '#')
                {
                    return Color.FromArgb((value.Length <= 7 ? unchecked((int)0xFF000000) : 0) +
                        Int32.Parse(value.Substring(1), System.Globalization.NumberStyles.HexNumber));
                }
                else
                {
                    return Color.FromName(value);
                }
            }
            catch (Exception)
            {
            }
    
            return Color.Black;
        }
    
        [XmlText]
        public string Default
        {
            get { return FromColor(color_); }
            set { color_ = ToColor(value); }
        }
    }
    

    【讨论】:

      【解决方案5】:

      我相信下面我有一个更简单的解决方案。颜色序列化被忽略,颜色作为简单的 32 位 ARGB 数据保存和加载。

      [XmlIgnore]
      public Color BackColor { get; set; }
      
      [XmlElement("BackColor")]
      public int BackColorAsArgb
      {
          get { return BackColor.ToArgb();  }
          set { BackColor = Color.FromArgb(value); }
      }
      

      【讨论】:

      • 这个解决方案仍然存在我想避免的问题,即代理属性。
      • 我很好奇您为什么要避免使用“代理属性”?
      • 他可能不知道每个属性都有一个(内部)字段来保存实际数据。
      【解决方案6】:

      对于那些使用System.Windows.Media.Color 的人,@bvj 的解决方案可以简化,利用类的ToString 方法:

          using System.Windows.Media;
      
          public class XmlColor
          {
              private Color m_color;
      
              public XmlColor() { }
              public XmlColor(Color c) { m_color = c; }
      
              public static implicit operator Color(XmlColor x)
              {
                  return x.m_color;
              }
      
              public static implicit operator XmlColor(Color c)
              {
                  return new XmlColor(c);
              }
      
              [XmlText]
              public string Default
              {
                  get { return m_color.ToString(); }
                  set { m_color = (Color)ColorConverter.ConvertFromString(value); }
              }
          }
      }
      

      和以前一样,现在您可以在每个可序列化的 Color 属性之前添加它:

      [XmlElement(Type = typeof(XmlColor))]

      默认情况下,System.Media.Color 序列化为此 XML:

      <DisplayColor>
        <A>255</A>
        <R>123</R>
        <G>0</G>
        <B>0</B>
        <ScA>1</ScA>
        <ScR>0.482352942</ScR>
        <ScG>0</ScG>
        <ScB>0</ScB>
      </DisplayColor>
      

      通过上述转换,它序列化为:

      <DisplayColor>#FF7B0000</DisplayColor>
      

      【讨论】:

        猜你喜欢
        • 2010-09-22
        • 1970-01-01
        • 1970-01-01
        • 2010-09-18
        • 2018-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多