How to get IXmlSerialize to not output element tag for null custom object

I have a custom object that is serialized by overriding the WriteXml function as below.
public void WriteXml(XmlWriter writer)
{
if ( !myObject.bHasValuel ) return;
write.WriteString( myObject.ToString );
}

However, this causes the output to contain an empty tag
<myObject />
What I need is to NOT have any tag output at all. The element is defined as IsNullable="true", but myObject is never null, but needs to be treated as null for output. How do I do this.
percentageAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

drichardsCommented:
Then you have to perform additional tests on myObject to determine whether to include it in the output.  What conditions indicate that you want it omitted from output?
0
percentageAuthor Commented:
myObject has a boolean property value that indicates if it contains valid data. If true, I want to output the XML element for the object. If false, I don't want to output any tag. This could be a method instead of a property.

myObject.bHasValue
or
myObject.HasValue()
0
drichardsCommented:
What is the context of this serialization (where is IsNullable set)?
0
Cloud Class® Course: Certified Penetration Testing

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

percentageAuthor Commented:
Below is the declaration of the test class I wrote to work on this problem. The problem is that when I serialize the object (o = new MyXml()) where the default values are null, the resulting XML looks like this:

<?xml version="1.0" encoding="utf-8"?>
<MYXML>
<Value1 />
<Value2 />
</MYXML>

What I want to get is this:
<?xml version="1.0" encoding="utf-8"?>
<MYXML>
</MYXML>

The problem is that Value1 and Value1, as objects, are not null, just the content of the object is null, therefore IsNullable does not really apply to this situation. There does not seem to be a way to tell WriteXml to not output the tags.
It looks like I need to override some function at a higher level than WriteXml(), but I can't find any documentation as to what that might be.

[XmlRootAttribute(ElementName="MYXML",Namespace="")]
public class MyXml
{
	[XmlElementAttribute( IsNullable = true )]
	public MyObject Value1 = new MyObject();
	[XmlElementAttribute( IsNullable = true )]
	public MyObject Value2 = new MyObject();
}
 
[XmlRootAttribute( IsNullable = true )]
public class MyObject : IXmlSerializable
{
	[XmlIgnore]
	public bool bHasValue = false;
 
	[XmlIgnore]
	private string m_Value = null;
 
	[XmlIgnore]
	public string sValue
	{
		get
		{
			if ( !bHasValue ) return ("");
			return (m_Value);
		}
		set
		{
			if ( value == null ) return;
			m_Value = value;
			bHasValue = true;
		}
	}
 
	public XmlSchema GetSchema()
	{
		return null;
	}
 
	public void ReadXml( XmlReader reader )
	{
		string sLocalName = reader.LocalName;
		sValue = reader.ReadElementString();
 
	}
	public void WriteXml( XmlWriter writer )
	{
		if ( bHasValue ) writer.WriteString( m_Value );
	}
}

Open in new window

0
drichardsCommented:
You need to initialize value1 and value2 to null.  If you wany values there, create and populate them externally and then "o.Value1 = <some instance of MyObject>" or "o.Value1 = <some instance of MyObject>".  The way you have it written, IsNullable is useless because Value1/2 will never be null.  Or you need to write a custom serializer for MyXml as well and only have is serialize Value1 and Value2 if they are not null and bHasValue = true.
0
percentageAuthor Commented:
I KNOW THAT IsNullable IS USELESS! Read my message.
I know that I need to write a custom serializer! Read my message.
HOW DO I WRITE A CUSTOM SERIALIZER????
0
drichardsCommented:
Point is you don't have to.  You just need to initialize MyXml differently.
0
drichardsCommented:
Actually, you DO need to implement IXmlSerializable on MyXml because you still get

<Value1 xsi:nil="true" />

event if you initialize Value1 to null.

So just implement IXmlSerializable on MyXml in addition to MyObject and do something like:

        if (Value1 != null && Value1.bHasValue) { Value1.WriteXml(writer); }
        if (Value2 != null && Value2.bHasValue) { Value2.WriteXml(writer); }

in WriteXml.
0
drichardsCommented:
You'll also have to change the serializer in MyObject to put in the open and close tags for MyObject since these don't get added automatically once you've got a custom serializer on MyXml.
    //WriteXml for MyObject:
    public void WriteXml(XmlWriter writer)
    {
        writer.WriteStartElement("MyObject");
        if (bHasValue) writer.WriteString(m_Value);
        writer.WriteEndElement();
    }
 
    //WriteXml for MyXML
    public void WriteXml(XmlWriter writer)
    {
        if (Value1 != null && Value1.bHasValue) { Value1.WriteXml(writer); }
        if (Value2 != null && Value2.bHasValue) { Value2.WriteXml(writer); }
    }

Open in new window

0
drichardsCommented:
It's too late to be doing this.  Anyway, here's the full classes that work:
[XmlRootAttribute(ElementName = "MYXML", Namespace = "")]
public class MyXml : IXmlSerializable
{
    [XmlElementAttribute(IsNullable = true)]
    public MyObject Value1 = new MyObject();
    [XmlElementAttribute(IsNullable = true)]
    public MyObject Value2 = new MyObject();
 
    #region IXmlSerializable Members
 
    public XmlSchema GetSchema()
    {
        throw new Exception("The method or operation is not implemented.");
    }
 
    public void WriteXml(XmlWriter writer)
    {
        XmlSerializer xs = new XmlSerializer(typeof(MyObject));
        if (Value1 != null && Value1.bHasValue) { xs.Serialize(writer, Value1); }
        if (Value2 != null && Value2.bHasValue) { xs.Serialize(writer, Value2); }
    }
 
    public void ReadXml(XmlReader reader)
    {
        throw new Exception("The method or operation is not implemented.");
    }
 
    #endregion
}
 
[XmlRootAttribute(IsNullable = true)]
public class MyObject : IXmlSerializable
{
    [XmlIgnore]
    public bool bHasValue = false;
 
    [XmlIgnore]
    private string m_Value = null;
 
    [XmlIgnore]
    public string sValue
    {
        get
        {
            if (!bHasValue) return ("");
            return (m_Value);
        }
        set
        {
            if (value == null) return;
            m_Value = value;
            bHasValue = true;
        }
    }
 
    public XmlSchema GetSchema()
    {
        return null;
    }
 
    public void ReadXml(XmlReader reader)
    {
        string sLocalName = reader.LocalName;
        sValue = reader.ReadElementString();
 
    }
    public void WriteXml(XmlWriter writer)
    {
        if (bHasValue) writer.WriteString(m_Value);
    }
}

Open in new window

0
percentageAuthor Commented:
That is much closer to what I need, but there are still a problem. When you serialize the object when it has values you get:

<MYXML>
<MyObject>One</MyObject>
<MyObject>Two</MyObject>
</MYXML>

How do you get the "Value1" and "Value2" names to come out in serialization?

Another issue is that I would have to modify all classes that contain MyObject which for the real application is considerable. Is there any higher level functions or events that occur earlier in the process that can be overridden when serializing MyObject in order to prevent the tag output?
0
drichardsCommented:
The reason that the standard serilization does not want to leave out either of the elements is that your XML is technically illegal if you do that.  You've got two MyObject elements defined in MyXml.  Suppose the first one is empty, and by your logic omitted from the XML, and the second one is included.  From the output, you cannot tell which one had the value and cannot reconstruct the class reliably.  This is because you have a schema violation - a sequence of two identical elements where they are both minOccurs=0, maxOccurs=1, which is illegal..

To be correct, you need to define a sequence of MyObject with minOccurs=0, maxOccurs=2.   In code this will translate into an array of MyObject.
0
percentageAuthor Commented:
You have me confused. I understand why the standard way of doing it does not work.
As for validity, the MyObject class is the class that is used for the values of the main class, not the name of the elements. The functinoality I am looking for is as follows

public class MyXml
{
public MyObject Value1 = new MyObject();
public MyObject Value2 = new MyObject();
}

which when serilized will produce one of the following

<MYXML>
<Value1 />
<Value2 />
</MYXML>

If one or more of the values is not valid I want the output to be

<MYXML>
<Value2 />
</MYXML>
or
<MYXML>
<Value1 />
</MYXML>
or
<MYXML>
</MYXML>
All of which are valid XML.

There is no ambiguity. The MyObject class that is used for Value1 and Value2 is a complex class that requires custom serialization, and that custom serialization needs to be able to omit the value tags when the class contains no valid data. The WriteXml() function does not seem to be able to do this which is why I am looking for another method.
0
drichardsCommented:
Yes, you can rename the MyObject elements, and that you solve the issue I was just talking about.  I'll post something for that.
0
drichardsCommented:
> the MyObject class is the class that is used for the values of the main class
But in XML serialization, it's also used as the element name unless you tell it otherwise.

What is the greater context for all this?  I included some code below which does what you want except that you'd need to change the way MyObject is used - note that the instances get initialized to null.  This means you'd have to create instances of MyObject prior to using them, which may not be feasible in your design.

If that is true, there really isn't a way around the issue other than major code changes.  Basically, the answer to your question (Is there any higher level functions or events that occur earlier in the process that can be overridden...) is no.  You're going to be stuck writing either custom serialization for every one of your classes or modifying the way they work to use default serialization.

For the latter, initialize Value1/2 to null.  Any classes trying to use Value1/2 would have to check them for null prior to use and create a new MyObject instance if Value1/2 were null.

And a question. Why the requirement not to have the empty elements in the XML?
    [XmlRootAttribute(ElementName = "MYXML", Namespace = "")]
    public class MyXml
    {
        [XmlElementAttribute(ElementName="Value1")]
        public MyObject Value1 = null;
        [XmlElementAttribute(ElementName = "Value2")]
        public MyObject Value2 = null;
 
    }
 
    public class MyObject : IXmlSerializable
    {
        [XmlIgnore]
        public bool bHasValue = false;
 
        private string m_Value = null;
 
        [XmlIgnore]
        public string sValue
        {
            get
            {
                if (!bHasValue) return ("");
                return (m_Value);
            }
            set
            {
                if (value == null) return;
                m_Value = value;
                bHasValue = true;
            }
        }
 
        #region IXmlSerializable Members
 
        public XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void ReadXml(XmlReader reader)
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void WriteXml(XmlWriter writer)
        {
            if (bHasValue) { writer.WriteString(m_Value); }
        }
 
        #endregion
    }

Open in new window

0
percentageAuthor Commented:
You are correct. Our custom classes are never null, even if they do not contain valid data.
We have a number of custom classes that extend the functionality of some basic system classes.

For instance we have a class called MyDateTime that has a number of custom methods that we use for our processing. However, when we create the XML for export to other systems (not ours), we need to serialize the class as if it were a System.DateTime class. The references to the class in our system is never null, so we need to do custom serialization to not have a tag output when there is no valid date in the object otherwise any other user of the XML, who will refer to the elemnt as a System.DateTime object will get a deserialization error.

In other words, if the schema has an element defined as

[XmlElementAttribute(IsNullable=true)
System.DateTime DateTimeValue;

and if the XML contains the following

<DateTimeValue />

deserialization throws an error because it can't deserialize an empty tag into a System.DateTime object.

This is just one instance of a number of custom classes that have the same problem/requirement.
0
drichardsCommented:
Then the object hierarchy used for deserialization does not match the one used for serialization.  Or put another way, you want to serialize using a schema that does not match the schema represented by your objects.

Your example of DateTime is bad, however, because it's serialization will never be an empty element.  It mayhave a bogus amd meaningless value, but it will never be empty.

Here is a solution that avoids custom serialization and still lets you initialize MyObject to non-null.  It requires the fields to be made private and the addition of some properties to control serialization properly.  This is what you would get if you started with schema and created the classes with the xsd utility (or pretty close, anyway - it would not have the logic in the "...Specified properties).

If this is again not feasible because you cannot change your existing classes, you have no option but totally custom serialization.
0
drichardsCommented:
Forgot to attach the code:
    [XmlRootAttribute(ElementName = "MYXML", Namespace = "")]
    public class MyXml// : IXmlSerializable
    {
        private MyObject Value1Field = new MyObject();
        private MyObject Value2Field = new MyObject();
 
        [XmlElementAttribute(ElementName = "Value1")]
        public MyObject Value1
        {
            get { return Value1Field; }
            set { Value1Field = value; }
        }
 
        [XmlElementAttribute(ElementName = "Value2")]
        public MyObject Value2
        {
            get { return Value2Field; }
            set { Value2Field = value; }
        }
 
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public bool Value1Specified
        {
            get { return Value1Field != null && Value1Field.bHasValue; }
        }
 
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public bool Value2Specified
        {
            get { return Value2Field != null &&  Value2Field.bHasValue; }
        }
 
    }
 
    public class MyObject : IXmlSerializable
    {
        [XmlIgnore]
        public bool bHasValue = false;
 
        private string m_Value = null;
 
        [XmlIgnore]
        public string sValue
        {
            get
            {
                if (!bHasValue) return ("");
                return (m_Value);
            }
            set
            {
                if (value == null) return;
                m_Value = value;
                bHasValue = true;
            }
        }
 
        #region IXmlSerializable Members
 
        public XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void ReadXml(XmlReader reader)
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void WriteXml(XmlWriter writer)
        {
            if (bHasValue) { writer.WriteString(m_Value); }
        }
 
        #endregion

Open in new window

0
drichardsCommented:
Actually, this might be a bit more palatable.  You only need to add the "...Specified" properties and not extra fields.
    [XmlRootAttribute(ElementName = "MYXML", Namespace = "")]
    public class MyXml// : IXmlSerializable
    {
        [XmlElementAttribute(ElementName = "Value1")]
        public MyObject Value1 = new MyObject();
        [XmlElementAttribute(ElementName = "Value2")]
        public MyObject Value2 = new MyObject();
 
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public bool Value1Specified
        {
            get { return Value1 != null && Value1.bHasValue; }
        }
 
        [System.Xml.Serialization.XmlIgnoreAttribute()]
        public bool Value2Specified
        {
            get { return Value2 != null &&  Value2.bHasValue; }
        }
 
    }
 
    public class MyObject : IXmlSerializable
    {
        [XmlIgnore]
        public bool bHasValue = false;
 
        private string m_Value = null;
 
        [XmlIgnore]
        public string sValue
        {
            get
            {
                if (!bHasValue) return ("");
                return (m_Value);
            }
            set
            {
                if (value == null) return;
                m_Value = value;
                bHasValue = true;
            }
        }
 
        #region IXmlSerializable Members
 
        public XmlSchema GetSchema()
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void ReadXml(XmlReader reader)
        {
            throw new Exception("The method or operation is not implemented.");
        }
 
        public void WriteXml(XmlWriter writer)
        {
            if (bHasValue) { writer.WriteString(m_Value); }
        }
 
        #endregion
    }

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
percentageAuthor Commented:
Thanks, this will accomplish what I need for now.
However it is still a lot of work as I have to add a "specified" function everywhere the usage of the class is optional (which is all cases in this instance).

I think I will persue a custom serialization solution as soon as I have the time.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.