How to serialize dynamic object to xml c# - c#

I have a object {System.Collections.Generic.List<object>} that contains 1000 object {DynamicData} inside of it, each one with 4 keys and values and one more List with 2 keys and values inside.
I need to serialize this object into a XML File, i tried normal serialization but it gives me this exception = The type DynamicData was not expected, how can i serialize this object?
Here is the code:
//output is the name of my object
XmlSerializer xsSubmit = new XmlSerializer(output.GetType());
var xml = "";
using (var sww = new StringWriter())
{
using (XmlWriter writers = XmlWriter.Create(sww))
{
try
{
xsSubmit.Serialize(writers, output);
}
catch (Exception ex)
{
throw;
}
xml = sww.ToString(); // Your XML
}
}
I can create the xml file writing line by line and element by element, but i want something more faster and with less code.
The structure of my object is like this:
output (count 1000)
[0]
Costumer - "Costumername"
DT - "Date"
Key - "Key"
Payment - "x"
[0]
Adress - "x"
Number - "1"
[1]...
[2]...

You can implement your own serialize object by using IXmlSerializable
[Serializable]
public class ObjectSerialize : IXmlSerializable
{
public List<object> ObjectList { get; set; }
public XmlSchema GetSchema()
{
return new XmlSchema();
}
public void ReadXml(XmlReader reader)
{
}
public void WriteXml(XmlWriter writer)
{
foreach (var obj in ObjectList)
{
//Provide elements for object item
writer.WriteStartElement("Object");
var properties = obj.GetType().GetProperties();
foreach (var propertyInfo in properties)
{
//Provide elements for per property
writer.WriteElementString(propertyInfo.Name, propertyInfo.GetValue(obj).ToString());
}
writer.WriteEndElement();
}
}
}
Usage;
var output = new List<object>
{
new { Sample = "Sample" }
};
var objectSerialize = new ObjectSerialize
{
ObjectList = output
};
XmlSerializer xsSubmit = new XmlSerializer(typeof(ObjectSerialize));
var xml = "";
using (var sww = new StringWriter())
{
using (XmlWriter writers = XmlWriter.Create(sww))
{
try
{
xsSubmit.Serialize(writers, objectSerialize);
}
catch (Exception ex)
{
throw;
}
xml = sww.ToString(); // Your XML
}
}
Output
<?xml version="1.0" encoding="utf-16"?>
<ObjectSerialize>
<Object>
<Sample>Sample</Sample>
</Object>
</ObjectSerialize>
Note : Be careful with that, if you want to deserialize with same type
(ObjectSerialize) you should provide ReadXml. And if you want to
specify schema, you should provide GetSchema too.

Related

Serializing a XML Document with 3 Levels

I am trying to get my output in a specified format for example
Expected
<?xml version="1.0"?>
<xml>
<JournalEntries>
<JournalEntry>
<Field1>SampleOne</Field1>
<Field2>SampleTwo</Field2>
</JournalEntry>
<JournalEntry>
<Field1>SampleOne</Field1>
<Field2>SampleTwo</Field2>
</JournalEntry>
</JournalEntries>
</xml>
My Current Output:
<?xml version="1.0"?>
<xml>
<JournalEntry>
<Field1>SampleOne</Field1>
<Field2>SampleTwo</Field2>
</JournalEntry>
<JournalEntry>
<Field1>SampleOne</Field1>
<Field2>SampleTwo</Field2>
</JournalEntry>
</xml>
So essentially I need to add another root ? in a sense so that there is one JournalEntries at the start and end. Since a group of entries consists of multiple Entry
[XmlRoot(ElementName = "xml")]
public class JournalDocument
{
public JournalDocument()
{
}
public JournalDocument(UnProcessedDocument input)
{
input.Body.ForEach(o =>
{
JournalEntries.Add(new JournalEntry
{
Field1 = "SampleOne"
Field2 = "SampleTwo"
}); ;
});
}
[XmlElement("JournalEntry")]
public List<JournalEntry> JournalEntries { get; set; } = new List<JournalEntry>();
}
public class JournalEntry
{
public string Field1 {get;set;}
public string Field2 {get;set}
}
I don't know if i have my defined root and elements in the correct place and i've tried moving them around but to no luck.. for example i tried putting [XmlRoot(ElementName = "JournalEntries")] just above my JournalEntry class
I prefer using IXmlSerializable.
It's more flexible to me and gives you much more control over the serialization.
Example:
public class JournalDocument : IXmlSerializable
{
public XmlSchema GetSchema() => null;
public void WriteXml(XmlWriter writer)
{
writer.WriteStartDocument();
writer.WriteStartElement("xml");
writer.WriteStartElement("JournalEntries")
if(this.JournalEntries.Count != 0)
{
foreach(JournalEntrie entrie in this.JournalEntries)
{
writer.WriteStartElement("JournalEntrie");
writer.WriteElementString("Field1", entrie.Field1)
writer.WriteElementString("Field2", entrie.Field2)
writer.WriteEndElement();
}
}
writer.WriteEndElement();
writer.WriteEndDocument(); // close all open elements
}
public void ReadXml(XmlReader reader)
{
while(reader.Read())
{
if(reader.NodeType == XmlNodeType.element)
{
JournalEntrie entrie;
switch(reader.Name)
{
case "JournalEntrie":
entrie = new JournalEntrie();
break;
case "Field1":
entire.Field1 = reader.ReadElementContentAsString();
break;
// Now make this for every element.
}
}
}
}
}
Although IXMLSerializer was an option I ended up creating an extension method instead
public static string Serialize<T>(this object obj)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
var xmlWriterSettings = new XmlWriterSettings() { Indent = true };
XmlSerializer serializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using var writer = XmlWriter.Create(stringWriter, xmlWriterSettings);
serializer.Serialize(writer, obj, ns);
writer.Close();
return stringWriter.ToString();
}
And my main code I could keep the same with [XmlRoot(ElementName = "xml")] but just remove the [XmlElement("JournalEntry")]
This ended up solving my issue.

Deserialize returns null key and null value (count is correct)

I am experimenting with C# serialization, and had to serialize a Dictionary, as it isn't serializable I used an XML string, which then gets deserialized (example taken from https://blogs.msdn.microsoft.com/adam/2010/09/10/how-to-serialize-a-dictionary-or-hashtable-in-c/)
DictionaryDataItem class:
[Serializable]
public class DictionaryDataItem
{
public string Key;
public string Value;
public DictionaryDataItem(string key, string value)
{
Key = key;
Value = value;
}
public DictionaryDataItem()
{
Key = "";
Value = "";
}
}
Serialization code:
[WebMethod]
public string ListarPaises()
{
List<DictionaryDataItem> Paises = new List<DictionaryDataItem>();
XmlSerializer serializer = new XmlSerializer(typeof(List<DictionaryDataItem>));
StringWriter sw = new StringWriter();
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
try
{
Dictionary<string, string> diccionario = FabricaLogica.getLogicaGeneral().ListarPaises();
foreach (string key in diccionario.Keys)
{
Paises.Add(new DictionaryDataItem(key, diccionario[key]));
}
serializer.Serialize(sw, Paises, ns);
} catch (Exception ex)
{
ThrowSoapException(ex);
}
return sw.ToString();
}
The serialization works and it returns the following string as xml:
<string xmlns="http://tempuri.org/">
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfDictionaryDataItem>
<DictionaryDataItem>
<Key>ARG</Key>
<Value>ARGENTINA</Value>
</DictionaryDataItem>
<DictionaryDataItem>
<Key>BRA</Key>
<Value>BRASIL</Value>
</DictionaryDataItem>
<DictionaryDataItem>
<Key>URU</Key>
<Value>URUGUAY</Value>
</DictionaryDataItem>
</ArrayOfDictionaryDataItem>
</string>
And then, my deserialization code:
Dictionary<string, string> Paises = new Dictionary<string, string>();
string RawData = new WebService.WebService().ListarPaises();
XmlSerializer xs = new XmlSerializer(typeof(List<DictionaryDataItem>));
StringReader sr = new StringReader(RawData);
List<DictionaryDataItem> templist = (List<DictionaryDataItem>)xs.Deserialize(sr);
foreach (DictionaryDataItem di in templist)
{
Paises.Add(di.Key, di.Value);
}
return Paises;
I can see that templist has 3 items, but both key and value for all of the items is null.
Am I missing something?

Add dynamically xml tag to serialized object without modifying the object

I have an object serializable like this :
[XmlRoot("MyObject")]
[Serializable]
public class MyObject
{
public MyObject()
{
}
[XmlAttribute("name")]
public string Name { get; set; }
}
Right now I am able to export this object in an xml file with the following code :
public byte[] Export(MyObject myObject)
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
byte[] value = null;
using (MemoryStream stream = new MemoryStream())
{
StreamWriter writer = new StreamWriter(stream);
serializer.Serialize(writer, myObject);
value = stream.ToArray();
}
return value;
}
For the moment, my exported xml is like this :
<MyObject Name="nameData">
I would like to export other data (a list of name related to myObject and obtained from another service) with this. But I cannot modify my MyObject class.
This is how I get the additionnal data :
public XElement GetExtraData(MyObject myObject)
{
List<string> datas = service.GetData(myObject);
var otherDatas = new XElement("otherDatas");
foreach (var data in datas)
{
var dataElement = new XElement("data");
var valueAttribute = new XAttribute("Value", data);
dataElement.Add(valueAttribute);
}
otherDatas.Add(dataElement);
}
I would like to append this otherDatas element to my previous element in order to obtain the following :
<MyObject Name="nameData">
<data Value="valueData">
<data Value="valueData">
<data Value="valueData">
But I don't see how, as this new method returns me a xElement created by me, and the natively .net serialization returns me an array of bytes.
Add the XElement returned from GetExtraData as Nodes to the original xml:
var doc = new XDocument(GetExtraData(new MyObject { Name = "someOtherName" }));
var byt = Export(new MyObject { Name = "nameData" });
using (var stream = new MemoryStream(byt))
{
var element = new XElement(XElement.Load(stream));
element.Add(doc.Nodes());
}
And save element

How to serialize ANY object into a string?

I'm running into an issue where my JSON serializer is failing randomly due to the character < showing up from time to time. I can't nail down where this is coming from and I want to - on exception - reserialize using a different method so I can see a full representation of the offending object. Is there any way to do this?
My current code:
// data is of type 'object'
serialized = JsonConvert.SerializeObject(data, new JsonSerializerSettings() {
Error = delegate(object sender, ErrorEventArgs args) {
// reserialize here and output object so I know what the heck is going on
}
})
There is no foolproof way to serialize any and every possible c# object.
Instead, you have a few ways to attack your problem:
Turn on Json.NET tracing. See Debugging with Serialization Tracing. This should tell you where in your object graph the problem is occurring.
Rather than serializing with JsonConvert.SerializeObject(), if you serialize with JsonSerializer.Serialize() and write to a string using a JsonTextWriter wrapping a StringWriter, you can flush the writer and log the partial serialization. That may give some idea where the problem arises.
You can try serializing using various other serializers, and if any work, log the result.
If one of your object properties is throwing an exception, you might try to force serialization of fields instead. See JSON.Net: Force serialization of all private fields and all fields in sub-classes.
For instance, putting #1, #2 and #3 together gives the following method:
public static class JsonSerializerExtensions
{
public static string SerializeObject(object obj, JsonSerializerSettings settings = null)
{
settings = settings ?? new JsonSerializerSettings();
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
using (var jsonWriter = new JsonTextWriter(writer))
{
var oldError = settings.Error;
var oldTraceWriter = settings.TraceWriter;
var oldFormatting = settings.Formatting;
try
{
settings.Formatting = Newtonsoft.Json.Formatting.Indented;
if (settings.TraceWriter == null)
settings.TraceWriter = new MemoryTraceWriter();
settings.Error = oldError + delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
{
jsonWriter.Flush();
var logSb = new StringBuilder();
logSb.AppendLine("Serialization error: ");
logSb.Append("Path: ").Append(args.ErrorContext.Path).AppendLine();
logSb.Append("Member: ").Append(args.ErrorContext.Member).AppendLine();
logSb.Append("OriginalObject: ").Append(args.ErrorContext.OriginalObject).AppendLine();
logSb.AppendLine("Error: ").Append(args.ErrorContext.Error).AppendLine();
logSb.AppendLine("Partial serialization results: ").Append(sb).AppendLine();
logSb.AppendLine("TraceWriter contents: ").Append(settings.TraceWriter).AppendLine();
logSb.AppendLine("JavaScriptSerializer serialization: ");
try
{
logSb.AppendLine(new JavaScriptSerializer().Serialize(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("XmlSerializer serialization: ");
try
{
logSb.AppendLine(obj.GetXml());
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
logSb.AppendLine("BinaryFormatter serialization: ");
try
{
logSb.AppendLine(BinaryFormatterExtensions.ToBase64String(obj));
}
catch (Exception ex)
{
logSb.AppendLine("Failed, error: ").AppendLine(ex.ToString());
}
Debug.WriteLine(logSb);
};
var serializer = JsonSerializer.CreateDefault(settings);
serializer.Serialize(jsonWriter, obj);
}
finally
{
settings.Error = oldError;
settings.TraceWriter = oldTraceWriter;
settings.Formatting = oldFormatting;
}
}
return sb.ToString();
}
}
public static class XmlSerializerExtensions
{
public static T LoadFromXML<T>(this string xmlString)
{
using (StringReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
public static string GetXml<T>(this T obj)
{
using (var textWriter = new StringWriter())
{
var settings = new XmlWriterSettings() { Indent = true, IndentChars = " " };
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
new XmlSerializer(obj.GetType()).Serialize(xmlWriter, obj);
return textWriter.ToString();
}
}
}
public static class BinaryFormatterExtensions
{
public static string ToBase64String<T>(T obj)
{
using (var stream = new MemoryStream())
{
new BinaryFormatter().Serialize(stream, obj);
return Convert.ToBase64String(stream.GetBuffer(), 0, checked((int)stream.Length)); // Throw an exception on overflow.
}
}
public static T FromBase64String<T>(string data)
{
return FromBase64String<T>(data, null);
}
public static T FromBase64String<T>(string data, BinaryFormatter formatter)
{
using (var stream = new MemoryStream(Convert.FromBase64String(data)))
{
formatter = (formatter ?? new BinaryFormatter());
var obj = formatter.Deserialize(stream);
if (obj is T)
return (T)obj;
return default(T);
}
}
}
You would likely replace the final Debug.WriteLine() with an appropriate logging method, then replace JsonConvert.SerializeObject(data) with JsonSerializerExtensions.SerializeObject(data) in your applications code.

read line and keep spaces from xml file

I am trying to write in xml file some profiles that i created so far so good ,
the input string is ProfilesList(0) = "45 65 67"
ProfilesList(1) = "profilename";
public void CreateGroupXML(String GroupNameWithPath, List<String> ProfilesList)
{
ProfilesGroup.ProfilesList = ProfilesList;
XmlWriterSettings ws = new XmlWriterSettings();
ws.NewLineHandling = NewLineHandling.Entitize;
for (int i = 0; i < ProfilesList.Count; i++)
{
ProfilesList[i] += Environment.NewLine;
}
XmlSerializer serializer = new XmlSerializer(typeof(ProfilesGroup));
using (XmlWriter wr = XmlWriter.Create(GroupNameWithPath, ws))
{
serializer.Serialize(wr, ProfilesGroup);
}
}
}
in the xml file the profiles written like that :
ProfilesList="45 65 67
profilename
So far so good, the problem happens when i trying read from the xml file
it split the first profile name into 3
here the code
public List<string> getProfilesOfGroup(string groupNameFullPath)
{
Stream stream = null;
try
{
stream = File.OpenRead(groupNameFullPath);
XmlSerializer serializer = new XmlSerializer(typeof(ProfilesGroup));
_ProfilesGroup = (ProfilesGroup)serializer.Deserialize(stream);
stream.Close();
return _ProfilesGroup.ProfilesList;
}
catch (Exception Ex)
{
log.ErrorFormat("Exception in getProfilesOfGroup: {0}", Ex.Message);
if (stream != null)
{
stream.Close();
}
return null;
}
}
the output (lets call the string ProfileList) contains :
ProfileList(0) = 45
ProfileList(1) = 65
ProfileList(2) = 67
ProfileList(3) = profilename
and i expecting the string to contain
ProfileList(0) = 45 65 67
ProfileList(1) = profilename
edit here the full xml :
?xml version="1.0" encoding="utf-8"?ProfilesGroup
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" ProfilesList="45 65
67
profilename
"
and the class:
[XmlRootAttribute("VProfilesGroup", IsNullable = false, DataType = "", Namespace = "")]
public class ProfilesGroup
{
[XmlAttribute("ProfilesList")]
public List<String> ProfilesList = new List<string>();
}
Why not just remove the [XmlAttribute("ProfilesList")] attribute? Your data will be serialized and deserialized successfully. The XML will look like:
<VProfilesGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ProfilesList>
<string>45 65 67</string>
<string>profilename</string>
</ProfilesList>
</VProfilesGroup>
In this format the list of strings is clearly defined to have two entries. This is the standard way to serialize & deserialize an array of strings with XmlSerializer. Or do you have some external constraint making you declare the list as an attribute?
Update
If you must serialize the ProfilesList as an attribute not an array of elements, you can manually construct and deconstruct the string like so:
[XmlRootAttribute("VProfilesGroup", IsNullable = false, DataType = "", Namespace = "")]
public class ProfilesGroup
{
static readonly char Delimiter = '\n';
[XmlIgnore]
public List<String> ProfilesList { get; set; } // Enhance the setter to throw an exception if any string contains the delimiter.
[XmlAttribute("ProfilesList")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string ProfilesListText
{
get
{
return string.Join(Delimiter.ToString(), ProfilesList.ToArray());
}
set
{
ProfilesList = new List<string>(value.Split(Delimiter));
}
}
public static string CreateGroupXML(List<String> ProfilesList)
{
var group = new ProfilesGroup();
group.ProfilesList = ProfilesList;
return XmlSerializationHelper.GetXml(group);
}
public static List<string> GetProfilesOfGroup(string xml)
{
XmlSerializer serializer = new XmlSerializer(typeof(ProfilesGroup));
var group = (ProfilesGroup)serializer.Deserialize(new StringReader(xml));
return group == null ? null : group.ProfilesList;
}
public static void Test()
{
List<string> list = new List<string>(new string[] { "45 65 67", "profilename" });
var xml = CreateGroupXML(list);
var newList = GetProfilesOfGroup(xml);
bool same = list.SequenceEqual(newList);
Debug.Assert(same); // No assert.
}
}
The resulting XML looks like:
<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n<VProfilesGroup xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" ProfilesList=\"45 65 67
profilename\" />
In this case, I am testing the code by serializing and deserializing to a string rather than to a file. And then the helper:
public static class XmlSerializationHelper
{
public static string GetXml<T>(T obj, XmlSerializer serializer) where T : class
{
using (var textWriter = new StringWriter())
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true; // For cosmetic purposes.
settings.IndentChars = " "; // The indentation used in the test string.
using (var xmlWriter = XmlWriter.Create(textWriter, settings))
{
serializer.Serialize(xmlWriter, obj);
}
return textWriter.ToString();
}
}
public static string GetXml<T>(T obj) where T : class
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
return GetXml(obj, serializer);
}
}

Categories

Resources