System.Version not serialized
Asked Answered
S

6

32

I've got a class with a System.Version property, which looks like this:

  • Version
    • Build: 111
    • Major: 1
    • MajorRevision: 0
    • Minor: 1
    • MinorRevision: 10
    • Revision: 10

When I serialize the class, version is always empty:

<Version />

The Client class looks like:

[Serializable]
public class Client
{
    public string Description;
    public string Directory;
    public DateTime ReleaseDate;
    public Version Version;
}
Shainashaine answered 18/1, 2010 at 12:13 Comment(0)
D
18

System.Version is not serializable, if you look at it's properties on MSDN, you'll see they have no setters...so the serializer won't store them. However, this approach still works. That article (old but still works) provides a Version class that is serializable, can you switch to that and get going?

Edit by tomfanning
I have fished the code from the dead site out of archive.org, reproduced below.

using System;
using System.Globalization;
namespace CubicOrange.Version
{
    /// <summary>
    /// Serializable version of the System.Version class.
    /// </summary>
    [Serializable]
    public class ModuleVersion : ICloneable, IComparable
    {
        private int major;
        private int minor;
        private int build;
        private int revision;
        /// <summary>
        /// Gets the major.
        /// </summary>
        /// <value></value>
        public int Major
        {
            get
            {
                return major;
            }
            set
            {
                major = value;
            }
        }
        /// <summary>
        /// Gets the minor.
        /// </summary>
        /// <value></value>
        public int Minor
        {
            get
            {
                return minor;
            }
            set
            {
                minor = value;
            }
        }
        /// <summary>
        /// Gets the build.
        /// </summary>
        /// <value></value>
        public int Build
        {
            get
            {
                return build;
            }
            set
            {
                build = value;
            }
        }
        /// <summary>
        /// Gets the revision.
        /// </summary>
        /// <value></value>
        public int Revision
        {
            get
            {
                return revision;
            }
            set
            {
                revision = value;
            }
        }
        /// <summary>
        /// Creates a new <see cref="ModuleVersion"/> instance.
        /// </summary>
        public ModuleVersion()
        {
            this.build = -1;
            this.revision = -1;
            this.major = 0;
            this.minor = 0;
        }
        /// <summary>
        /// Creates a new <see cref="ModuleVersion"/> instance.
        /// </summary>
        /// <param name="version">Version.</param>
        public ModuleVersion(string version)
        {
            this.build = -1;
            this.revision = -1;
            if (version == null)
            {
                throw new ArgumentNullException("version");
            }
            char[] chArray1 = new char[1] { '.' };
            string[] textArray1 = version.Split(chArray1);
            int num1 = textArray1.Length;
            if ((num1 < 2) || (num1 > 4))
            {
                throw new ArgumentException("Arg_VersionString");
            }
            this.major = int.Parse(textArray1[0], CultureInfo.InvariantCulture);
            if (this.major < 0)
            {
                throw new ArgumentOutOfRangeException("version", "ArgumentOutOfRange_Version");
            }
            this.minor = int.Parse(textArray1[1], CultureInfo.InvariantCulture);
            if (this.minor < 0)
            {
                throw new ArgumentOutOfRangeException("version", "ArgumentOutOfRange_Version");
            }
            num1 -= 2;
            if (num1 > 0)
            {
                this.build = int.Parse(textArray1[2], CultureInfo.InvariantCulture);
                if (this.build < 0)
                {
                    throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
                }
                num1--;
                if (num1 > 0)
                {
                    this.revision = int.Parse(textArray1[3], CultureInfo.InvariantCulture);
                    if (this.revision < 0)
                    {
                        throw new ArgumentOutOfRangeException("revision", "ArgumentOutOfRange_Version");
                    }
                }
            }
        }
        /// <summary>
        /// Creates a new <see cref="ModuleVersion"/> instance.
        /// </summary>
        /// <param name="major">Major.</param>
        /// <param name="minor">Minor.</param>
        public ModuleVersion(int major, int minor)
        {
            this.build = -1;
            this.revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
            }
            this.major = major;
            this.minor = minor;
            this.major = major;
        }
        /// <summary>
        /// Creates a new <see cref="ModuleVersion"/> instance.
        /// </summary>
        /// <param name="major">Major.</param>
        /// <param name="minor">Minor.</param>
        /// <param name="build">Build.</param>
        public ModuleVersion(int major, int minor, int build)
        {
            this.build = -1;
            this.revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
            }
            if (build < 0)
            {
                throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
            }
            this.major = major;
            this.minor = minor;
            this.build = build;
        }
        /// <summary>
        /// Creates a new <see cref="ModuleVersion"/> instance.
        /// </summary>
        /// <param name="major">Major.</param>
        /// <param name="minor">Minor.</param>
        /// <param name="build">Build.</param>
        /// <param name="revision">Revision.</param>
        public ModuleVersion(int major, int minor, int build, int revision)
        {
            this.build = -1;
            this.revision = -1;
            if (major < 0)
            {
                throw new ArgumentOutOfRangeException("major", "ArgumentOutOfRange_Version");
            }
            if (minor < 0)
            {
                throw new ArgumentOutOfRangeException("minor", "ArgumentOutOfRange_Version");
            }
            if (build < 0)
            {
                throw new ArgumentOutOfRangeException("build", "ArgumentOutOfRange_Version");
            }
            if (revision < 0)
            {
                throw new ArgumentOutOfRangeException("revision", "ArgumentOutOfRange_Version");
            }
            this.major = major;
            this.minor = minor;
            this.build = build;
            this.revision = revision;
        }
        #region ICloneable Members
        /// <summary>
        /// Clones this instance.
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            ModuleVersion version1 = new ModuleVersion();
            version1.major = this.major;
            version1.minor = this.minor;
            version1.build = this.build;
            version1.revision = this.revision;
            return version1;
        }
        #endregion
        #region IComparable Members
        /// <summary>
        /// Compares to.
        /// </summary>
        /// <param name="obj">Obj.</param>
        /// <returns></returns>
        public int CompareTo(object version)
        {
            if (version == null)
            {
                return 1;
            }
            if (!(version is ModuleVersion))
            {
                throw new ArgumentException("Arg_MustBeVersion");
            }
            ModuleVersion version1 = (ModuleVersion)version;
            if (this.major != version1.Major)
            {
                if (this.major > version1.Major)
                {
                    return 1;
                }
                return -1;
            }
            if (this.minor != version1.Minor)
            {
                if (this.minor > version1.Minor)
                {
                    return 1;
                }
                return -1;
            }
            if (this.build != version1.Build)
            {
                if (this.build > version1.Build)
                {
                    return 1;
                }
                return -1;
            }
            if (this.revision == version1.Revision)
            {
                return 0;
            }
            if (this.revision > version1.Revision)
            {
                return 1;
            }
            return -1;
        }
        #endregion
        /// <summary>
        /// Equalss the specified obj.
        /// </summary>
        /// <param name="obj">Obj.</param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if ((obj == null) || !(obj is ModuleVersion))
            {
                return false;
            }
            ModuleVersion version1 = (ModuleVersion)obj;
            if (((this.major == version1.Major) && (this.minor == version1.Minor)) && (this.build == version1.Build) && (this.revision == version1.Revision))
            {
                return true;
            }
            return false;
        }
        /// <summary>
        /// Gets the hash code.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            int num1 = 0;
            num1 |= ((this.major & 15) << 0x1c);
            num1 |= ((this.minor & 0xff) << 20);
            num1 |= ((this.build & 0xff) << 12);
            return (num1 | this.revision & 0xfff);
        }
        /// <summary>
        /// Operator ==s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator ==(ModuleVersion v1, ModuleVersion v2)
        {
            return v1.Equals(v2);
        }
        /// <summary>
        /// Operator &gt;s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator >(ModuleVersion v1, ModuleVersion v2)
        {
            return (v2 < v1);
        }
        /// <summary>
        /// Operator &gt;=s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator >=(ModuleVersion v1, ModuleVersion v2)
        {
            return (v2 <= v1);
        }
        /// <summary>
        /// Operator !=s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator !=(ModuleVersion v1, ModuleVersion v2)
        {
            return (v1 != v2);
        }
        /// <summary>
        /// Operator &lt;s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator <(ModuleVersion v1, ModuleVersion v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) < 0);
        }
        /// <summary>
        /// Operator &lt;=s the specified v1.
        /// </summary>
        /// <param name="v1">V1.</param>
        /// <param name="v2">V2.</param>
        /// <returns></returns>
        public static bool operator <=(ModuleVersion v1, ModuleVersion v2)
        {
            if (v1 == null)
            {
                throw new ArgumentNullException("v1");
            }
            return (v1.CompareTo(v2) <= 0);
        }
        /// <summary>
        /// Toes the string.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            if (this.build == -1)
            {
                return this.ToString(2);
            }
            if (this.revision == -1)
            {
                return this.ToString(3);
            }
            return this.ToString(4);
        }
        /// <summary>
        /// Toes the string.
        /// </summary>
        /// <param name="fieldCount">Field count.</param>
        /// <returns></returns>
        public string ToString(int fieldCount)
        {
            object[] objArray1;
            switch (fieldCount)
            {
                case 0:
                    {
                        return string.Empty;
                    }
                case 1:
                    {
                        return (this.major.ToString());
                    }
                case 2:
                    {
                        return (this.major.ToString() + "." + this.minor.ToString());
                    }
            }
            if (this.build == -1)
            {
                throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "2"), "fieldCount");
            }
            if (fieldCount == 3)
            {
                objArray1 = new object[5] { this.major, ".", this.minor, ".", this.build };
                return string.Concat(objArray1);
            }
            if (this.revision == -1)
            {
                throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "3"), "fieldCount");
            }
            if (fieldCount == 4)
            {
                objArray1 = new object[7] { this.major, ".", this.minor, ".", this.build, ".", this.revision };
                return string.Concat(objArray1);
            }
            throw new ArgumentException(string.Format("ArgumentOutOfRange_Bounds_Lower_Upper {0},{1}", "0", "4"), "fieldCount");
        }
    }
}
Darin answered 18/1, 2010 at 12:17 Comment(1)
I also submitted an edit that includes a TypeConverter, which enables ModuleVersion to be used as an ApplicationSetting. Doing so is optional depending on how you plan to use it, but having it here may be beneficial to someone down the road.Melendez
P
17

I prefer to use approach below, so I have only one property with Type VersionXml in my class. Implicit operators are really useful here.

[Serializable]
[XmlType("Version")]
public class VersionXml
{
    public VersionXml()
    {
        this.Version = null;
    }

    public VersionXml(Version Version)
    {
        this.Version = Version;
    }

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

    [XmlText]
    [EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
    public string Value
    {
        get { return this.Version == null ? string.Empty : this.Version.ToString(); }
        set
        {
            Version temp;
            Version.TryParse(value, out temp);
            this.Version = temp;
        }
    }

    public static implicit operator Version(VersionXml VersionXml)
    {
        return VersionXml.Version;
    }

    public static implicit operator VersionXml(Version Version)
    {
        return new VersionXml(Version);
    }

    public override string ToString()
    {
        return this.Value;
    }
}
Plaque answered 23/9, 2013 at 14:42 Comment(2)
Very neat solution! Just a hint for anyone wondering how to use it: [XmlElement(Type = typeof(VersionXml))] public Version SomeVersionProperty { get; set; }Byproduct
You can just use public VersionXml SomeVersionProperty { get; set; } and use a.SomeVersionProperty = new Version(...) since there are implicit operators in the VersionXml classPlaque
S
9

You can use string proxy property:

[XmlIgnore]
public System.Version Version { get; set; }
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
public string version 
{
    get 
    {
        if (this.Version == null)
            return string.Empty;
        else
            return this.Version.ToString();
    }
    set 
    {
        if(!String.IsNullOrEmpty(value))
           this.Version = new Version(value);
    } 
}
Shortsighted answered 23/9, 2013 at 13:6 Comment(1)
This is, in practice, a more useful solution than the accepted answer. You don't need to make a new class. If you've got an existing System.Version property and all you need to do is make XML Serialization work, this is much less disruptive.Duodenum
P
3

More precisely, System.Version is not XML-serializable. It is serializable with a BinaryFormatter, for example:

Version version = new Version(1, 2, 3, 4);

using (MemoryStream stream = new MemoryStream())
{
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(stream, version);

    stream.Position = 0;

    Version deserialized = (Version)formatter.Deserialize(stream);
}
Prosthesis answered 9/8, 2013 at 8:0 Comment(0)
A
1

You need to define your get and set accessors, as:

public class Version
{
    public int Build { get; set; }
    public int Major { get; set; }
    public int MajorRevision { get; set; }
    public int Minor { get; set; }
    public int MinorRevision { get; set; }
    public int Revision { get; set; }
}

// ...

new XmlSerializer(typeof (Version))
    .Serialize(Console.Out,
               new Version()
                   {
                       Build = 111,
                       Major = 1,
                       MajorRevision = 0,
                       Minor = 1,
                       MinorRevision = 10,
                       Revision = 10
                   }
    );

I got this output:

<?xml version="1.0" encoding="ibm850"?>
<Version xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Build>111</Build>
  <Major>1</Major>
  <MajorRevision>0</MajorRevision>
  <Minor>1</Minor>
  <MinorRevision>10</MinorRevision>
  <Revision>10</Revision>
</Version>
Astrogeology answered 18/1, 2010 at 12:19 Comment(1)
Sorry, I didn't noticed your're using System.VersionAstrogeology
C
1

I have cleaned up Nick Craver's implementation to be much more concise and readable, added XML Serialization support (with attributes instead of separate elements), implemented a string TypeConverter as well as fixed a couple comparison issues that had the possibility for NullReferenceExceptions or infinite loops (calling the overloaded operators from within themselves).

I also used some C# 6 features as well.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Xml.Serialization;

[Serializable]
[TypeConverter(typeof(VersionCodeConverter))]
public class VersionCode : ICloneable, IComparable<VersionCode>, IEquatable<VersionCode>
{
  [XmlAttribute] public int Major { get; private set; }
  [XmlAttribute] public int Minor { get; private set; }
  [XmlAttribute] public int Build { get; private set; } = -1;
  [XmlAttribute] public int Revision { get; private set; } = -1;

  public VersionCode() { }

  public VersionCode(int major, int minor, int build = 0, int revision = 0)
  {
    if (major < 0)
      throw new ArgumentOutOfRangeException(nameof(major), $"{nameof(major)} cannot be less than 0");

    if (minor < 0)
      throw new ArgumentOutOfRangeException(nameof(minor), $"{nameof(minor)} cannot be less than 0");

    if (build < 0)
      throw new ArgumentOutOfRangeException(nameof(build), $"{nameof(build)} cannot be less than 0");

    if (revision < 0)
      throw new ArgumentOutOfRangeException(nameof(revision), $"{nameof(revision)} cannot be less than 0");

    Major = major;
    Minor = minor;
    Build = build;
    Revision = revision;
  }

  public VersionCode(string version)
  {
    if (version == null)
      throw new ArgumentNullException(nameof(version));

    var components = new Stack<string>(version.Split('.'));

    if (components.Count < 2 || components.Count > 4)
      throw new ArgumentException(nameof(version));

    Major = int.Parse(components.Pop(), CultureInfo.InvariantCulture);

    if (Major < 0)
      throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Major)} cannot be less than 0");

    Minor = int.Parse(components.Pop(), CultureInfo.InvariantCulture);

    if (Minor < 0)
      throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Minor)} cannot be less than 0");

    if (!components.Any())
      return;

    Build = int.Parse(components.Pop(), CultureInfo.InvariantCulture);

    if (Build < 0)
      throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Build)} cannot be less than 0");

    if (!components.Any())
      return;

    Revision = int.Parse(components.Pop(), CultureInfo.InvariantCulture);

    if (Revision < 0)
      throw new ArgumentOutOfRangeException(nameof(version), $"{nameof(Revision)} cannot be less than 0");
  }

  public object Clone()
    => new VersionCode(Major, Minor, Build, Revision);

  public int CompareTo(VersionCode version) 
    => Major != version.Major ? Major.CompareTo(version.Major)
      : Minor != version.Minor ? Minor.CompareTo(version.Minor)
        : Build != version.Build ? Build.CompareTo(version.Build)
          : Revision.CompareTo(version.Revision);

  public override bool Equals(object obj)
    => obj is VersionCode && Equals((VersionCode)obj);

  public bool Equals(VersionCode version)
    => Major == version.Major
    && Minor == version.Minor
    && Build == version.Build
    && Revision == version.Revision;

  public override int GetHashCode()
  {
    var hash = 0;

    hash |= (Major & 15) << 0x1c;
    hash |= (Minor & 0xff) << 20;
    hash |= (Build & 0xff) << 12;
    hash |= (Revision & 0xfff);

    return hash;
  }

  public static bool operator ==(VersionCode v1, VersionCode v2)
    => ReferenceEquals(v1, null) ? ReferenceEquals(v2, null) : v1.Equals(v2);

  public static bool operator !=(VersionCode v1, VersionCode v2)
    => !(v1 == v2);

  public static bool operator >(VersionCode v1, VersionCode v2)
    => v2 < v1;

  public static bool operator >=(VersionCode v1, VersionCode v2)
    => v2 <= v1;

  public static bool operator <(VersionCode v1, VersionCode v2)
    => !ReferenceEquals(v1, null) && v1.CompareTo(v2) < 0;

  public static bool operator <=(VersionCode v1, VersionCode v2)
    => !ReferenceEquals(v1, null) && v1.CompareTo(v2) <= 0;

  public override string ToString()
    => Build < 0 ? $"{Major}.{Minor}"
      : Revision < 0 ? $"{Major}.{Minor}.{Build}"
        : $"{Major}.{Minor}.{Build}.{Revision}";
}

public class VersionCodeConverter : TypeConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    => sourceType == typeof(string)
    || base.CanConvertFrom(context, sourceType);

  public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
  {
    var version = value as string;

    return version != null 
      ? new VersionCode(version)
      : base.ConvertFrom(context, culture, value);
  }
}
Coltoncoltsfoot answered 22/11, 2015 at 11:51 Comment(1)
I believe you constructor take the wrong order by using a Stack. We used a Queue instead: public VersionCode(string version) { var components = new Queue<string>(version.Split('.')); Major = int.Parse(components.Dequeue(), CultureInfo.InvariantCulture); ...Molybdic

© 2022 - 2024 — McMap. All rights reserved.