Самая элегантная XML-сериализация структуры Color - PullRequest
36 голосов
/ 19 июля 2010

Одна проблема достала меня достаточно, чтобы зарегистрироваться в Stack Overflow. В настоящее время, если я хочу сериализовать цвет в строку 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 с атрибутами C #, связанными с XML?

Ответы [ 6 ]

61 голосов
/ 01 декабря 2010

Вот что я использую для сериализации структуры Color в XML. По моему мнению, это лучше, чем дублирование основного свойства 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; }
}
18 голосов
/ 24 августа 2012

Я думаю, что ниже у меня есть более простое решение этого вопроса.Сериализация цвета игнорируется, а цвет сохраняется и загружается как простые 32-битные данные ARGB.

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

[XmlElement("BackColor")]
public int BackColorAsArgb
{
    get { return BackColor.ToArgb();  }
    set { BackColor = Color.FromArgb(value); }
}
5 голосов
/ 19 июля 2010

Боль, не правда ли?Это все, что вы можете сделать с XmlSerializer, если вы не реализуете IXmlSerializable (что я и делаю , а не рекомендую).Опции:

  • придерживайтесь этого, но также пометьте color_XmlSurrogate как [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] - это остановит его появление в большинстве представлений привязки данных и в редакторе кода при ссылке на вашу сборку какdll
  • использует DataContractSerializer, который поддерживает частные свойства (но который не поддерживает атрибуты xml; вы не можете выиграть ...)

Кстати, я бы имел color как свойство, а не поле:

[XmlIgnore]
public Color Color {get;set;}
4 голосов
/ 26 декабря 2011

Так как это мой первый вопрос, я решил изучить его подробнее.@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); }
    }
}
2 голосов
/ 26 декабря 2011

Я нашел другое решение,

Возможно использовать System.Windows.Media.Color вместо System.Drawing.Color .
Это сериализуемо в XML.

0 голосов
/ 11 января 2019

Для тех, кто использует 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>
...