C# XML Serialization of an array - Skip "empty" string values - c#

I want to skip the empty element creation in XML file during the xml Serialization in C#.
Ex :
ArrayElements elm = new ArrayElements
{ Items =new string[] "Item1", " ", "Item2", " ", " ", "Items" } };
During the serialization it should skip the empty strings.
This is my class :
[Serializable]
public class ArrayElements
{
[XmlElement(IsNullable=false)]
public string[] Items { get; set; }
}

You can do it with a surrogate property.
namespace Cheeso.Xml.Samples.Arrays
{
static class Extensions
{
private static XmlSerializerNamespaces _ns;
static Extensions()
{
// to suppress unused default namespace entries in the root elt
_ns = new XmlSerializerNamespaces();
_ns.Add( "", "" );
}
public static string SerializeToString(this XmlSerializer s, object o)
{
var builder = new System.Text.StringBuilder();
var settings = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, settings))
{
s.Serialize(writer, o, _ns);
}
return builder.ToString();
}
}
[XmlType("ArrayOfString")]
public class MyContainer
{
[XmlIgnore]
public String[] a;
// surrogate property
[XmlElement("string")]
public String[] A
{
get
{
List<String> _proxy = new List<String>();
foreach (var s in a)
{
if (!String.IsNullOrEmpty(s))
_proxy.Add(s);
}
return _proxy.ToArray();
}
set
{
a = value;
}
}
}
class StringArrayOnlyNonEmptyItems
{
static void Main(string[] args)
{
try
{
Console.WriteLine("\nRegular Serialization of an array of strings with some empty elements:");
String[] x = { "AAA", "BBB", "", "DDD", "", "FFF" };
XmlSerializer s1 = new XmlSerializer(typeof(String[]));
string s = s1.SerializeToString(x);
Console.WriteLine(s);
Console.WriteLine("\nSerialization of an array of strings with some empty elements, via a Container:");
MyContainer c = new MyContainer();
c.a = x;
XmlSerializer s2 = new XmlSerializer(typeof(MyContainer));
s = s2.SerializeToString(c);
Console.WriteLine(s);
}
catch (System.Exception exc1)
{
Console.WriteLine("uncaught exception:\n{0}", exc1);
}
}
}
}

Are you sure this is what you want? One drawback, is that when you deserialize, you will not be able to get back the empty strings, since there's no way for the deserializer to know about them. Usually when you deserialize, you want to get back an instance that looks exactly like the one you originally serialized.
If that is what you want, then you have to tweak your object to suit the serialization process. As Cheeso suggests, a surrogate property is a good solution for this.
Also, just for clarity, am I correct to say you have an object ArrayElements that has a property Items, which is an array of strings?

Related

How can i make a flat object by removing keys in C#?

I am really not sure if this could be achievable, How can i remove some nested keys in an Object and make the object very flat. I have a dynamic object as follows,
EventData": { "ChangeSet": { "Change": {
"changes": [
] } } }
and i want to change the above to
EventData": { [] }
is this can be achieved in C#?
Use the NewtonSoft.JSon package.. Following code does the trick. I made it a string array because I do not know what you need but you can change this to your liking.
const string complex = "{\"EventData\": { \"ChangeSet\": { \"Change\": { \"changes\" : [ ]}}}}";
Call to method:
string simple = returnSimpleObject(complex);
public class SerializeData
{
public string[] EventData { get; set; }
}
private static string returnSimpleObject(string Json)
{
JObject jobject = JObject.Parse(Json);
JToken tEventData = jobject.SelectToken("EventData");
SerializeData myEvent = tEventData.ToObject<SerializeData>();
JToken tchanges = jobject.SelectToken("EventData.ChangeSet.Change.changes");
myEvent.EventData = tchanges.ToObject<string[]>();
JsonSerializer serializer = new JsonSerializer();
StringWriter strWrite = new StringWriter();
JsonWriter myWriter = new JsonTextWriter(strWrite);
serializer.Serialize(myWriter, myEvent);
return strWrite.ToString();
}

XML serializing Enum type properties

I'm trying to XML serialize a class containing a enum property. If the property is declared using a specific enum, it works just fine. But I need the property to be of type Enum, so I can set it to different enum types. However, when doing this I get an exception.
The type [namespace].Simple may not be used in this context.
I've tried different attributes on the enum definition, but haven't gotten it right so far. Is there a way to do this?
public enum Simple : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
public Enum Simple { get; set; }
}
public class Program
{
static void Main(string[] args)
{
using (var writer = XmlWriter.Create(Console.OpenStandardOutput()))
{
try
{
var foo = new Foo
{
Simple = Simple.three
};
var serializer = new XmlSerializer(foo.GetType());
serializer.Serialize(writer, foo);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}
You can try to set EnumMember attrubute on your DataContract that you want to serialize, for more specific info visit
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.enummemberattribute(v=vs.110).aspx
Enum is abstract and cannot be serialized. A possible approach to solve is presented in this answer.
The common primitive base type of enum is int (by default, can also be byte or long for instance).
So you could as well simply use this integer base type instead, like byte Simple in your Foo class.
In case you need the string representation to appear in xml (identical to the enum field name), expose it as string Simple.
Based on dlatikay's idea about splitting the enum into two strings for enum type and member name, I've come up with the following solution. The example converts to from a Foo object to XML string, and back to a new Foo object again.
public enum SimpleEnum : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
private Enum _simple;
[XmlIgnore]
public Enum Simple
{
get { return _simple; }
set {
_simple = value;
var type = Simple.GetType();
var underlyingType = Enum.GetUnderlyingType(type);
EnumType = Simple.GetType().FullName;
EnumMember = Simple.ToString();
}
}
private string _enumType;
public string EnumType
{
get { return _enumType; }
set { _enumType = value; }
}
private string _enumMember;
public string EnumMember
{
get { return _enumMember; }
set {
_enumMember = value;
_simple = (Enum)Enum.Parse(Type.GetType(EnumType), EnumMember);
}
}
}
public class Program
{
static void Main(string[] args)
{
var str = new StringBuilder();
using (var writer = XmlWriter.Create(str))
{
try
{
var foo = new Foo
{
Simple = SimpleEnum.three
};
var serializer = new XmlSerializer(typeof(Foo));
serializer.Serialize(writer, foo);
Console.WriteLine(str.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
using (TextReader reader = new StringReader(str.ToString()))
{
try
{
var serializer = new XmlSerializer(typeof(Foo));
var foo = (Foo)serializer.Deserialize(reader);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}

Writing enumerable to csv file

I'm sure its very straightforward but I am struggling to figure out how to write an array to file using CSVHelper.
I have a class for example
public class Test
{
public Test()
{
data = new float[]{0,1,2,3,4};
}
public float[] data{get;set;}
}
i would like the data to be written with each array value in a separate cell. I have a custom converter below which is instead providing one cell with all the values in it.
What am I doing wrong?
public class DataArrayConverter<T> : ITypeConverter
{
public string ConvertToString(TypeConverterOptions options, object value)
{
var data = (T[])value;
var s = string.Join(",", data);
}
public object ConvertFromString(TypeConverterOptions options, string text)
{
throw new NotImplementedException();
}
public bool CanConvertFrom(Type type)
{
return type == typeof(string);
}
public bool CanConvertTo(Type type)
{
return type == typeof(string);
}
}
To further detail the answer from Josh Close, here what you need to do to write any IEnumerable (including arrays and generic lists) in a recent version (anything above 3.0) of CsvHelper!
Here the class under test:
public class Test
{
public int[] Data { get; set; }
public Test()
{
Data = new int[] { 0, 1, 2, 3, 4 };
}
}
And a method to show how this can be saved:
static void Main()
{
using (var writer = new StreamWriter("db.csv"))
using (var csv = new CsvWriter(writer))
{
var list = new List<Test>
{
new Test()
};
csv.Configuration.HasHeaderRecord = false;
csv.WriteRecords(list);
writer.Flush();
}
}
The important configuration here is csv.Configuration.HasHeaderRecord = false;. Only with this configuration you will be able to see the data in the csv file.
Further details can be found in the related unit test cases from CsvHelper.
In case you are looking for a solution to store properties of type IEnumerable with different amounts of elements, the following example might be of any help:
using CsvHelper;
using CsvHelper.Configuration;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace CsvHelperSpike
{
class Program
{
static void Main(string[] args)
{
using (var writer = new StreamWriter("db.csv"))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.Delimiter = ";";
var list = new List<AnotherTest>
{
new AnotherTest("Before String") { Tags = new List<string> { "One", "Two", "Three" }, After="After String" },
new AnotherTest("This is still before") {After="after again", Tags=new List<string>{ "Six", "seven","eight", "nine"} }
};
csv.Configuration.RegisterClassMap<TestIndexMap>();
csv.WriteRecords(list);
writer.Flush();
}
using(var reader = new StreamReader("db.csv"))
using(var csv = new CsvReader(reader))
{
csv.Configuration.IncludePrivateMembers = true;
csv.Configuration.RegisterClassMap<TestIndexMap>();
var result = csv.GetRecords<AnotherTest>().ToList();
}
}
private class AnotherTest
{
public string Before { get; private set; }
public string After { get; set; }
public List<string> Tags { get; set; }
public AnotherTest() { }
public AnotherTest(string before)
{
this.Before = before;
}
}
private sealed class TestIndexMap : ClassMap<AnotherTest>
{
public TestIndexMap()
{
Map(m => m.Before).Index(0);
Map(m => m.After).Index(1);
Map(m => m.Tags).Index(2);
}
}
}
}
By using the ClassMap it is possible to enable HasHeaderRecord (the default) again. It is important to note here, that this solution will only work, if the collection with different amounts of elements is the last property. Otherwise the collection needs to have a fixed amount of elements and the ClassMap needs to be adapted accordingly.
This example also shows how to handle properties with a private set. For this to work it is important to use the csv.Configuration.IncludePrivateMembers = true; configuration and have a default constructor on your class.
Unfortunately, it doesn't work like that. Since you are returning , in the converter, it will quote the field, as that is a part of a single field.
Currently the only way to accomplish what you want is to write manually, which isn't too horrible.
foreach( var test in list )
{
foreach( var item in test.Data )
{
csvWriter.WriteField( item );
}
csvWriter.NextRecord();
}
Update
Version 3 has support for reading and writing IEnumerable properties.

How to serialize/de-serialize a Dictionary<string, object> into XML

I am trying to write a save routine for my application where several parts of the application add items to a Dictionary and then the save function writes them to a XML file. The open routine needs to read those files and re-populate the Dictionary and I can then place those objects back into my application. I am struggling with the de-serialization of the routine I have now. My save routine is as follows
XmlDocument xmlDoc = new XmlDocument();
// Write down the XML declaration
XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
// Create the root element
XmlElement rootNode = xmlDoc.CreateElement("TireStudy");
xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
xmlDoc.AppendChild(rootNode);
foreach (var saveItem in _SaveItems)
{
XPathNavigator nav = rootNode.CreateNavigator();
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(saveItem.Value.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, saveItem.Value);
writer.Close();
}
}
xmlDoc.Save(fileName);
This routine works to create a file, but I would like the key value of the dictionary to be saved in the file as well and I am not sure how to de-serialize the file this creates because I do not know the types of the objects before I read them.
Part 2 (I hate adding new parts to a question, but I don't see a better way to address the problems going forward)
I now have the following code,
var knownTypes = new List<Type>
{
typeof(ObservableCollection<string>),
typeof(ObservableCollection<Segments>),
typeof(Segments),
typeof(List<string>)
};
var serialized = _SaveItems.Serialize(knownTypes);
but I get the following exception
Type 'System.Collections.Generic.List`1[System.String]' cannot be added to list of known types since another type 'System.Collections.ObjectModel.ObservableCollection`1[System.String]' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfstring' is already present. If there are different collections of a particular type - for example, List<Test> and Test[], they cannot both be added as known types. Consider specifying only one of these types for addition to the known types list.
If I delete either the typeof(ObservableCollection) or the typeof(List) it exceptions complaining it needs the one I deleted.
You could use DataContractSerializer as explained in this post but you may have to pass the known types as a parameter to the serializer to support nested object typed classes:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.IO;
using System.Xml;
public static class SerializationExtensions
{
public static string Serialize<T>(this T obj, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(obj.GetType(), knownTypes);
using (var writer = new StringWriter())
using (var stm = new XmlTextWriter(writer))
{
serializer.WriteObject(stm, obj);
return writer.ToString();
}
}
public static T Deserialize<T>(this string serialized, IEnumerable<Type> knownTypes)
{
var serializer = new DataContractSerializer(typeof(T), knownTypes);
using (var reader = new StringReader(serialized))
using (var stm = new XmlTextReader(reader))
{
return (T)serializer.ReadObject(stm);
}
}
}
public class Address
{
public string Country { get; set; }
public string City { get; set; }
}
public class CodedAddress
{
public int CountryCode { get; set; }
public int CityCode { get; set; }
}
class Program
{
static void Main(string[] args)
{
var persons1 = new Dictionary<string, Address>();
persons1.Add("John Smith", new Address { Country = "US", City = "New York" });
persons1.Add("Jean Martin", new Address { Country = "France", City = "Paris" });
// no need to provide known types to the serializer
var serializedPersons1 = persons1.Serialize(null);
var deserializedPersons1 = serializedPersons1.Deserialize<Dictionary<string, Address>>(null);
var persons2 = new Dictionary<string, object>();
persons2.Add("John Smith", new Address { Country = "US", City = "New York" });
persons2.Add("Jean Martin", new CodedAddress { CountryCode = 33, CityCode = 75 });
// must provide known types to the serializer
var knownTypes = new List<Type> { typeof(Address), typeof(CodedAddress) };
var serializedPersons2 = persons2.Serialize(knownTypes);
var deserializedPersons2 = serializedPersons2.Deserialize<Dictionary<string, object>>(knownTypes);
}
}

How to convert a List<T> to specific Json format

I want to be able to convert a List<T> into a specific JSON table-like format. In my case, the T will always be a simple object (no nested properties). Here are two examples to illustrate what I want.
Example #1: List<Person> to JSON
// C# list of Persons
var list = new List<Person>() {
new Person() { First = "Jesse", Last = "Gavin", Twitter = "jessegavin" },
new Person() { First = "John", Last = "Sheehan", Twitter = "johnsheehan" }
};
// I want to transform the list above into a JSON object like so
{
columns : ["First", "Last", "Twitter"],
rows: [
["Jesse", "Gavin", "jessegavin"],
["John", "Sheehan", "johnsheehan"]
]
}
Example #2: List<Address> to JSON
// C# list of Locations
var list = new List<Location>() {
new Location() { City = "Los Angeles", State = "CA", Zip = "90210" },
new Location() { City = "Saint Paul", State = "MN", Zip = "55101" },
};
// I want to transform the list above into a JSON object like so
{
columns : ["City", "State", "Zip"],
rows: [
["Los Angeles", "CA", "90210"],
["Saint Paul", "MN", "55101"]
]
}
Is there a way to tell JSON.net to serialize an object in this manner? If not, how could I accomplish this? Thanks.
UPDATE:
Thanks to #Hightechrider's answer, I was able to write some code that solves the problem.
You can view a working example here https://gist.github.com/1153155
Using reflection you can get a list of properties for the type:
var props = typeof(Person).GetProperties();
Given an instance of a Person p you can get an enumeration of the property values thus:
props.Select(prop => prop.GetValue(p, null))
Wrap those up in a generic method, add your favorite Json serialization and you have the format you want.
Assuming your using .Net 4 this should do everything you want. The class actually lets you convert to either XML or JSON. The Enum for CommunicationType is at the bottom. The serializer works best if the class your passing it has been decorated with DataContract & DataMember attributes. I've included a sample at the bottom. It will also take an anonymous type so long as it's all simple types.
Reflection would work as well but then you have to understand all the JSON nuances to output complex data types, etc. This used the built-in JSON serializer in .Net 4. One more note, because JSON does not define a date type .Net puts dates in a funky ASP.Net custom format. So long as your deserializing using the built-in deserializer it works just fine. I can dig up the documentation on that if you need.
using System;
using System.Xml.Serialization;
using System.Text;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Xml.Linq;
internal class Converter
{
public static string Convert<T>(T obj, CommunicationType format, bool indent = false, bool includetype = false)
{
if (format == CommunicationType.XML)
{
return ToXML<T>(obj, includetype, indent);
}
else if (format == CommunicationType.JSON)
{
return ToJSON<T>(obj);
}
else
{
return string.Empty;
}
}
private static string ToXML<T>(T obj, bool includetype, bool indent = false)
{
if (includetype)
{
XElement xml = XMLConverter.ToXml(obj, null, includetype);
if(indent) {
return xml.ToString();
}
else
{
return xml.ToString(SaveOptions.DisableFormatting);
}
}
else
{
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
XmlSerializer xs = new XmlSerializer(typeof(T));
StringBuilder sbuilder = new StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Indent = indent };
ns.Add(string.Empty, string.Empty);
using (var writer = System.Xml.XmlWriter.Create(sbuilder, xmlws))
{
xs.Serialize(writer, obj, ns);
}
string result = sbuilder.ToString();
ns = null;
xs = null;
sbuilder = null;
xmlws = null;
return result;
}
}
private static string ToJSON<T>(T obj)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
string result = string.Empty;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
ser.WriteObject(ms, obj);
result = encoding.GetString(ms.ToArray());
ms.Close();
encoding = null;
ser = null;
return result;
}
}
}
[DataContract()]
public enum CommunicationType : int
{
[XmlEnum("0"), EnumMember(Value = "0")]
XML = 0,
[XmlEnum("1"), EnumMember(Value = "1")]
JSON = 1
}
[DataContract(Namespace = "")]
public partial class AppData
{
[DataMember(Name = "ID")]
public string ID { get; set; }
[DataMember(Name = "Key")]
public string Key { get; set; }
[DataMember(Name = "Value")]
public string Value { get; set; }
[DataMember(Name = "ObjectType")]
public string ObjectType { get; set; }
}
Any specific reason why you don't need the standard format?
To actually answer the question:
Since this is something that is outside of JSON syntax I can't think of a way to implement this within the default framework.
One solution would be to leverage attributes decorate the properties you want transported over the wired with a custom attribute and using Reflection cycle through the properties and output their property names as the column headers and then cycle throw the objects and write the values. Generic enough so it could be applied across other objects as well.
public class Location
{
[JsonFooAttribute("City")]
public string city {get;set;}
[JsonFooAttribute("State")]
public string state {get;set;}
}

Categories