Xml serialization - Hide null values - c#

When using a standard .NET Xml Serializer, is there any way I can hide all null values? The below is an example of the output of my class. I don't want to output the nullable integers if they are set to null.
Current Xml output:
<?xml version="1.0" encoding="utf-8"?>
<myClass>
<myNullableInt p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
<myOtherInt>-1</myOtherInt>
</myClass>
What I want:
<?xml version="1.0" encoding="utf-8"?>
<myClass>
<myOtherInt>-1</myOtherInt>
</myClass>

You can create a function with the pattern ShouldSerialize{PropertyName} which tells the XmlSerializer if it should serialize the member or not.
For example, if your class property is called MyNullableInt you could have
public bool ShouldSerializeMyNullableInt()
{
return MyNullableInt.HasValue;
}
Here is a full sample
public class Person
{
public string Name {get;set;}
public int? Age {get;set;}
public bool ShouldSerializeAge()
{
return Age.HasValue;
}
}
Serialized with the following code
Person thePerson = new Person(){Name="Chris"};
XmlSerializer xs = new XmlSerializer(typeof(Person));
StringWriter sw = new StringWriter();
xs.Serialize(sw, thePerson);
Results in the followng XML - Notice there is no Age
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Chris</Name>
</Person>

Additionally to what Chris Taylor wrote: if you have something serialized as an attribute, you can have a property on your class named {PropertyName}Specified to control if it should be serialized. In code:
public class MyClass
{
[XmlAttribute]
public int MyValue;
[XmlIgnore]
public bool MyValueSpecified;
}

It exists a property called XmlElementAttribute.IsNullable
If the IsNullable property is set to true, the xsi:nil attribute is generated for class members that have been set to a null reference.
The following example shows a field with the XmlElementAttribute applied to it, and the IsNullable property set to false.
public class MyClass
{
[XmlElement(IsNullable = false)]
public string Group;
}
You can have a look to other XmlElementAttribute for changing names in serialization etc.

You can define some default values and it prevents the fields from being serialized.
[XmlElement, DefaultValue("")]
string data;
[XmlArray, DefaultValue(null)]
List<string> data;

I prefer creating my own xml with no auto-generated tags. In this I can ignore creating the nodes with null values:
public static string ConvertToXML<T>(T objectToConvert)
{
XmlDocument doc = new XmlDocument();
XmlNode root = doc.CreateNode(XmlNodeType.Element, objectToConvert.GetType().Name, string.Empty);
doc.AppendChild(root);
XmlNode childNode;
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
foreach (PropertyDescriptor prop in properties)
{
if (prop.GetValue(objectToConvert) != null)
{
childNode = doc.CreateNode(XmlNodeType.Element, prop.Name, string.Empty);
childNode.InnerText = prop.GetValue(objectToConvert).ToString();
root.AppendChild(childNode);
}
}
return doc.OuterXml;
}

In my case the nullable variables/elements were all String type. So, I simply performed a check and assigned them string.Empty in case of NULL. This way I got rid of the unnecessary nil and xmlns attributes (p3:nil="true" xmlns:p3="http://www.w3.org/2001/XMLSchema-instance)
// Example:
myNullableStringElement = varCarryingValue ?? string.Empty
// OR
myNullableStringElement = myNullableStringElement ?? string.Empty

private static string ToXml(Person obj)
{
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
string retval = null;
if (obj != null)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true }))
{
new XmlSerializer(obj.GetType()).Serialize(writer, obj,namespaces);
}
retval = sb.ToString();
}
return retval;
}

Related

Serialize c# object in XML using variable content as attribute name

I have the following c# object:
class Modification {
public string Name;
public string Value;
}
I want to use the serializer to serialize my object the following way:
<name>value</name>
Example: Let's say we set those variables to
Name = "Autoroute"
Value = 53
I want the xml to look like:
<test>
<Autoroute>53</Autoroute>
</test>
I saw somewhere that this feature is not supported by the serializer, but is there a way to overload the serializer to allow this kind of behavior ?
Changing the XML structure is not an option since it is already a convention.
You can use IXmlSerializable to do this, though this doesn't give you control over the root element name - you have to set this in the serializer (which may present other challenges when you come to read it as part of a larger xml structure...).
public class Modification : IXmlSerializable
{
public string Name;
public string Value;
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
reader.ReadStartElement();
Name = reader.Name;
Value = reader.ReadElementContentAsString();
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteElementString(Name, Value);
}
}
Usage,
Modification modification = new Modification()
{
Name = "Autoroute",
Value = "53"
};
Modification andBack = null;
string rootElement = "test";
XmlSerializer s = new XmlSerializer(typeof(Modification), new XmlRootAttribute(rootElement));
using (StreamWriter writer = new StreamWriter(#"c:\temp\output.xml"))
s.Serialize(writer, modification);
using (StreamReader reader = new StreamReader(#"c:\temp\output.xml"))
andBack = s.Deserialize(reader) as Modification;
Console.WriteLine("{0}={1}", andBack.Name, andBack.Value);
The XML produced by this looks like this,
<?xml version="1.0" encoding="utf-8"?>
<test>
<Autoroute>53</Autoroute>
</test>

Xml serialization remove inner tags

I'm having a bit of trouble getting the desired xml serialization that I want. Thanks beforehand for your help.
So, the xml that I am aiming for looks something like this:
<ChangeSet>
<Change class="object" key="foo">
bar
</Change>
<Change class="testing" key="temp">
temp
</Change>
</ChangeSet>
What I'm actually getting is:
<ChangeSet>
<Change class="object" key="foo">
<Value> bar </Value>
</Change>
<Change class="testing" key="temp">
<Value> temp </Value>
</Change>
</ChangeSet>
Note that the value inside of the Value tags need to be able to be any object. (Collection, object, generic type... etc)
How can I get rid of the Value tags?
C# code:
[Serializable]
[XmlRoot("ChangeSet")]
public class ChangeSet
{
[XmlElement("Change", typeof(Change))]
public List<Change> Changes;
}
public class Change
{
[XmlAttribute("Class")]
public string Class;
[XmlAttribute("Description")]
public string Key;
public object Value;
}
StringBuilder xml = new StringBuilder();
XmlSerializer serializer = new XmlSerializer(objToSerialize.GetType());
XmlWriterSettings settings = new XmlWriterSettings()
{
OmitXmlDeclaration = true,
Indent = true
};
using (StringWriter writer = new StringWriter(xml))
{
using (XmlWriter xmlWriter = XmlWriter.Create(writer, settings))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", ""); // Removes the xsd & xsi namespace declarations from the xml
serializer.Serialize(xmlWriter, objToSerialize, ns);
}
}
Use [XmlText] attribute over Value of type string
[XMLText]
public string Value;
or use another string property and ignore the Value property
[XMLIgnore]
public object Value;
[XMLText]
public string ValueString
{
get{ return this.Value.ToString(); }
}

Can int property be not written to XML during serialization if it is zero? [duplicate]

When using a standard .NET Xml Serializer, is there any way I can hide all null values? The below is an example of the output of my class. I don't want to output the nullable integers if they are set to null.
Current Xml output:
<?xml version="1.0" encoding="utf-8"?>
<myClass>
<myNullableInt p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance" />
<myOtherInt>-1</myOtherInt>
</myClass>
What I want:
<?xml version="1.0" encoding="utf-8"?>
<myClass>
<myOtherInt>-1</myOtherInt>
</myClass>
You can create a function with the pattern ShouldSerialize{PropertyName} which tells the XmlSerializer if it should serialize the member or not.
For example, if your class property is called MyNullableInt you could have
public bool ShouldSerializeMyNullableInt()
{
return MyNullableInt.HasValue;
}
Here is a full sample
public class Person
{
public string Name {get;set;}
public int? Age {get;set;}
public bool ShouldSerializeAge()
{
return Age.HasValue;
}
}
Serialized with the following code
Person thePerson = new Person(){Name="Chris"};
XmlSerializer xs = new XmlSerializer(typeof(Person));
StringWriter sw = new StringWriter();
xs.Serialize(sw, thePerson);
Results in the followng XML - Notice there is no Age
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Chris</Name>
</Person>
Additionally to what Chris Taylor wrote: if you have something serialized as an attribute, you can have a property on your class named {PropertyName}Specified to control if it should be serialized. In code:
public class MyClass
{
[XmlAttribute]
public int MyValue;
[XmlIgnore]
public bool MyValueSpecified;
}
It exists a property called XmlElementAttribute.IsNullable
If the IsNullable property is set to true, the xsi:nil attribute is generated for class members that have been set to a null reference.
The following example shows a field with the XmlElementAttribute applied to it, and the IsNullable property set to false.
public class MyClass
{
[XmlElement(IsNullable = false)]
public string Group;
}
You can have a look to other XmlElementAttribute for changing names in serialization etc.
You can define some default values and it prevents the fields from being serialized.
[XmlElement, DefaultValue("")]
string data;
[XmlArray, DefaultValue(null)]
List<string> data;
I prefer creating my own xml with no auto-generated tags. In this I can ignore creating the nodes with null values:
public static string ConvertToXML<T>(T objectToConvert)
{
XmlDocument doc = new XmlDocument();
XmlNode root = doc.CreateNode(XmlNodeType.Element, objectToConvert.GetType().Name, string.Empty);
doc.AppendChild(root);
XmlNode childNode;
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
foreach (PropertyDescriptor prop in properties)
{
if (prop.GetValue(objectToConvert) != null)
{
childNode = doc.CreateNode(XmlNodeType.Element, prop.Name, string.Empty);
childNode.InnerText = prop.GetValue(objectToConvert).ToString();
root.AppendChild(childNode);
}
}
return doc.OuterXml;
}
In my case the nullable variables/elements were all String type. So, I simply performed a check and assigned them string.Empty in case of NULL. This way I got rid of the unnecessary nil and xmlns attributes (p3:nil="true" xmlns:p3="http://www.w3.org/2001/XMLSchema-instance)
// Example:
myNullableStringElement = varCarryingValue ?? string.Empty
// OR
myNullableStringElement = myNullableStringElement ?? string.Empty
private static string ToXml(Person obj)
{
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add(string.Empty, string.Empty);
string retval = null;
if (obj != null)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb, new XmlWriterSettings() { OmitXmlDeclaration = true }))
{
new XmlSerializer(obj.GetType()).Serialize(writer, obj,namespaces);
}
retval = sb.ToString();
}
return retval;
}

How to include null properties during xml serialization

Currently, the code below omits null properties during serialization. I want null valued properties in the output xml as empty elements. I searched the web but didn't find anything useful. Any help would be appreciated.
var serializer = new XmlSerializer(application.GetType());
var ms = new MemoryStream();
var writer = new StreamWriter(ms);
serializer.Serialize(writer, application);
return ms;
Sorry, I forgot to mention that I want to avoid attribute decoration.
Can you control the items that have to be serialized?
Using
[XmlElement(IsNullable = true)]
public string Prop { get; set; }
you can represent it as <Prop xsi:nil="true" />
You can use also use the following code. The pattern is ShouldSerialize{PropertyName}
public class PersonWithNullProperties
{
public string Name { get; set; }
public int? Age { get; set; }
public bool ShouldSerializeAge()
{
return true;
}
}
PersonWithNullProperties nullPerson = new PersonWithNullProperties() { Name = "ABCD" };
XmlSerializer xs = new XmlSerializer(typeof(nullPerson));
StringWriter sw = new StringWriter();
xs.Serialize(sw, nullPerson);
XML
<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://
www.w3.org/2001/XMLSchema">
<Name>ABCD</Name>
<Age xsi:nil="true" />
</Person>
Set the XmlElementAttribute.IsNullable property:
https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlelementattribute.isnullable(v=vs.110).aspx

.Net XmlSerializer: deserialize CDATA being inner text

I have a problem with CDATA deserialization using standard .Net XmlSerializer.
Update: I get XML from external system and I can't influence it's format so I can't make CData be enclosed in a separate Element of Attribute.
Serialization gives this:
<?xml version="1.0" encoding="utf-16"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><![CDATA[Hello, world!]]></MyClass>
Deserialization does not restore object to it's original state.
Here's class that is being serialized:
public class MyClass
{
string _data;
[XmlIgnore]
public string Data
{
get { return _data; }
set { _data = value; }
}
[XmlAnyElement]
public XmlCDataSection CData
{
get { return new XmlDataDocument().CreateCDataSection(Data); }
set { Data = value.Value; }
}
}
Here's the test which fails:
[Test]
public void CData_as_inner_text_test()
{
MyClass item = new MyClass();
item.Data = "Hello, world!";
XmlSerializer serializer = new XmlSerializer(item.GetType());
string serialized;
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, item);
serialized = sw.GetStringBuilder().ToString();
}
MyClass deserialized;
using (StringReader sr = new StringReader(serialized))
{
deserialized = (MyClass)serializer.Deserialize(sr);
}
Assert.AreEqual(item.Data, deserialized.Data); // For some reason, deserialized.Data == null
}
I found the same problem here but there's no answer:
XmlSerializer, XmlAnyElement and CDATA
The CData property ends up null, because the content of the CDATA section ends up in the Data property, where it is being ignored...
<MyClass><![CDATA[Hello, world!]]></MyClass>
is absolutely equivalent to:
<MyClass>Hello, world!</MyClass>
You shouldn't care whether the external app writes the content of MyClass as CData or not. Likewise, the external app shouldn't care how you write it out.
IOW, this should be all you need:
public class MyClass
{
string _data;
[XmlText]
public string Data
{
get { return _data; }
set { _data = value; }
}
}
First declare a property as XmlCDataSection
public XmlCDataSection ProjectXml { get; set; }
in this case projectXml is a string xml
ProjectXml = new XmlDocument().CreateCDataSection(projectXml);
when you serialize your message you will have your nice format (notice )
<?xml version="1.0" encoding="utf-16"?>
<MessageBase xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="Message_ProjectStatusChanged">
<ID>131</ID>
<HandlerName>Plugin</HandlerName>
<NumRetries>0</NumRetries>
<TriggerXml><![CDATA[<?xml version="1.0" encoding="utf-8"?><TmData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Version="9.0.0" Date="2012-01-31T15:46:02.6003105" Format="1" AppVersion="10.2.0" Culture="en-US" UserID="0" UserRole=""><PROJECT></PROJECT></TmData>]]></TriggerXml>
<MessageCreatedDate>2012-01-31T20:28:52.4843092Z</MessageCreatedDate>
<MessageStatus>0</MessageStatus>
<ProjectId>0</ProjectId>
<UserGUID>8CDF581E44F54E8BAD60A4FAA8418070</UserGUID>
<ProjectGUID>5E82456F42DC46DEBA07F114F647E969</ProjectGUID>
<PriorStatus>0</PriorStatus>
<NewStatus>3</NewStatus>
<ActionDate>0001-01-01T00:00:00</ActionDate>
</MessageBase>

Categories

Resources