Problem
I’m working with an XML structure that requires booleans to be represented as 1 or 0.
So, suppose the XML output has to look like:
<valid>1</valid>
Several nodes can be represented this way, so I made a struct to handle this. The solution I found works, but lacks oomph. OK, no, it doesn’t just lack oomph, it sucks.
(Implemented in .NET 4)
public struct MyCrappyBool : IXmlSerializable
{
private int _IntValue;
private bool _BoolValue;
public int IntValue
{
get { return _IntValue; }
set
{
if (value < 0 || value > 1)
throw new ArgumentOutOfRangeException();
_IntValue = value;
_BoolValue = value == 1;
}
}
public bool BoolValue
{
get { return _BoolValue; }
set
{
_BoolValue = value;
_IntValue = value ? 1 : 0;
}
}
public MyCrappyBool(int intValue) : this()
{
IntValue = intValue;
}
public MyCrappyBool(bool boolValue): this()
{
BoolValue = boolValue;
}
#region IXmlSerializable Members
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
IntValue = int.Parse(reader.ReadString());
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(IntValue.ToString());
}
#endregion
}
This is incredibly tedious, first and foremost because I need to instantiate every time:
MyCrappyBool isValid = new MyCrappyBool(true);
But even then, some nifty implicit overloading can address that:
public static implicit operator MyCrappyBool(bool boolValue)
{
return new MyCrappyBool(boolValue);
}
public static implicit operator MyCrappyBool(int intValue)
{
return new MyCrappyBool(intValue);
}
That’s still a lot of code for one… crappy boolean.
Later, it can be (XML) serialized as part of any other class:
[XmlElement("valid")]
public MyCrappyBool IsValid { get; set; }
[XmlElement("active")]
public MyCrappyBool IsActive { get; set; }
Is there a simpler way about this I’m not seeing?
Update: Reverse implicit operator overloading!
public static implicit operator bool(MyCrappyBool myCrappyBool)
{
return myCrappyBool.BoolValue;
}
public static implicit operator int(MyCrappyBool myCrappyBool)
{
return myCrappyBool.IntValue;
}
This means all my properties can be private.
Solution
I’d implement it like this:
public struct Bit : IEquatable<Bit>, IXmlSerializable
{
private int value;
public Bit(bool value) { this.value = value ? 1 : 0; }
public Bit(int value) : this(value != 0) { }
public bool Equals(Bit other) { return this.value == other.value; }
public override bool Equals(object obj) { return obj is Bit && this.Equals((Bit)obj); }
public override int GetHashCode() { return value.GetHashCode(); }
public override string ToString() { return this.value.ToString(); }
public static implicit operator int(Bit value) { return value.value; }
public static explicit operator bool(Bit value) { return value.value != 0; }
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { throw new NotImplementedException(); }
void IXmlSerializable.ReadXml(XmlReader reader) { this.value = reader.ReadElementContentAsInt(); }
void IXmlSerializable.WriteXml(XmlWriter writer) { writer.WriteValue(this.value); }
}
It would be better to store your value as an integer as that is the “true” value that you’re interested in. It’s value is normalized using C semantics (i.e., false
if 0
, true
otherwise as opposed to true
if 1
, false
otherwise). That would feel more natural to me but it’s up to you.
I’d be careful with the operator overloading. Don’t do it just because you can, do it because it makes sense. I’d stop at implementing casting operators from the type explicitly (since that’s the only way to get at the value), use the constructors otherwise. And make only one of them an implicit cast, otherwise you will run into ambiguity errors if you use this a lot in your code.
Consider implementing the IEquatable<T>
interface and other appropriate overrides. If you intend to use this in your code, this could be invaluable.
Consider implementing the IXmlSerializable
interface explicitly. If you intend to use this type as you would any other in your code, you probably wouldn’t want to see the serialization methods all the time. Otherwise if you are only using this for serialization, implement it implicitly.
And now for something completely different. While this does not have the flexibility of implicit conversion to and from bool, I think it’s the next best thing:
public enum BoolEnum
{
[XmlEnum("0")]
False = 0,
[XmlEnum("1")]
True = 1
}
And… that’s it! It serializes BoolEnum.True
as “1”, and can deserialize from it too. Error catching is nonexistent, though. If the value in the file should be other than 1 or 0, an InvalidOperationException
inside another is thrown. If there was a way to customize that exception, I’d like to know it.
Here’s a possible answer although I’m not sure it goes far enough for you
public struct MyCrappyBool : IXmlSerializable
{
public bool Value
{
get; private set;
}
public MyCrappyBool(object v)
{
bool value = false;
if(Boolean.TryParse(v, out value))
Value = value;
else
throw new ArgumentException("Parameter not a valid boolean value");
}
#region IXmlSerializable Members
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
Value = int.Parse(reader.ReadString()) == 1;
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(Value ? "1" : "0");
}
#endregion
}
I would constrain the type of the element in the xml by specifying it’s allowed values in the XSD definition. Otherwise I’d just have another SetBoolean(int) method that through the exception if the int value was not 0.
I’m sure there are other ways. I’m not sure if the System.Xml.Serialization.XmlElementAttribute attribute would already do this for you but may be worth looking into.
Based on dreza’s ideas, I now have: (and somehow I see this better as an answer than an edit on the question):
public struct MyCrappyBool : IXmlSerializable
{
private bool Value;
public MyCrappyBool(int intValue) : this(intValue == 1)
{ }
public MyCrappyBool(bool boolValue) : this()
{
Value = boolValue;
}
public override string ToString()
{
//return ((int)this).ToString();
return Value ? "1" : "0";
}
public static implicit operator MyCrappyBool(bool boolValue)
{
return new MyCrappyBool(boolValue);
}
public static implicit operator MyCrappyBool(int intValue)
{
return new MyCrappyBool(intValue);
}
public static implicit operator bool(MyCrappyBool myCrappyBool)
{
return myCrappyBool.Value;
}
public static implicit operator int(MyCrappyBool myCrappyBool)
{
return myCrappyBool ? 1 : 0;
}
#region IXmlSerializable Members
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader reader)
{
Value = int.Parse(reader.ReadString()) == 1;
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(ToString());
}
#endregion
}
To be honest, ((int)this).ToString()
seems convoluted and evil.