I am having following peice of code ,where in i am trying to serialize and deserailize object of StringResource class.
Please note Resource1.stringXml = its coming from resource file.If i pass strelemet.outerXMl i get the object from Deserialize object ,but if i pass Resource1.stringXml i am getting following exception
{"< STRING xmlns=''> was not expected."} System.Exception {System.InvalidOperationException}
class Program
{
static void Main(string[] args)
{
StringResource str = new StringResource();
str.DELETE = "CanDelete";
str.ID= "23342";
XmlElement strelemet = SerializeObjectToXmlNode (str);
StringResource strResourceObject = DeSerializeXmlNodeToObject<StringResource>(Resource1.stringXml);
Console.ReadLine();
}
public static T DeSerializeXmlNodeToObject<T>(string objectNodeOuterXml)
{
try
{
TextReader objStringsTextReader = new StringReader(objectNodeOuterXml);
XmlSerializer stringResourceSerializer = new XmlSerializer(typeof(T),string.Empty);
return (T)stringResourceSerializer.Deserialize(objStringsTextReader);
}
catch (Exception excep)
{
return default(T);
}
}
public static XmlElement SerializeObjectToXmlNode(object obj)
{
using (MemoryStream memoryStream = new MemoryStream())
{
try
{
XmlSerializerNamespaces xmlNameSpace = new XmlSerializerNamespaces();
xmlNameSpace.Add(string.Empty, string.Empty);
XmlWriterSettings writerSettings = new XmlWriterSettings();
writerSettings.CloseOutput = false;
writerSettings.Encoding = System.Text.Encoding.UTF8;
writerSettings.Indent = false;
writerSettings.OmitXmlDeclaration = true;
XmlWriter writer = XmlWriter.Create(memoryStream, writerSettings);
XmlSerializer xmlserializer = new XmlSerializer(obj.GetType());
xmlserializer.Serialize(writer, obj, xmlNameSpace);
writer.Close();
memoryStream.Position = 0;
XmlDocument serializeObjectDoc = new XmlDocument();
serializeObjectDoc.Load(memoryStream);
return serializeObjectDoc.DocumentElement;
}
catch (Exception excep)
{
return null;
}
}
}
}
public class StringResource
{
[XmlAttribute]
public string DELETE;
[XmlAttribute]
public string ID;
}
< STRING ID="1" DELETE="True" />
Problem is a name mismatch in your XML root node and your class
< STRING ID="1" DELETE="True" /> -- STRING here
public class StringResource -- StringResource Here.
Add xmlroot attribute to your class, and will see what happens
[XmlRoot( "STRING " )]
public class StringResource
{
[XmlAttribute]
public string DELETE;
[XmlAttribute]
public string ID;
}
Wel it's obvious Resource1.stringXml does not contain a correct XML which could be deserialized to a StringResource object. The XML should look like this:
<StringResource DELETE="CanDelete" ID="23342" />
Related
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.
I have two classes. One of the classes is containing an array of the other class.
I dont want the first Class to be serialized, only the array of the other class, so I am passing the array to the Serialization-Method, but it seems to be loosing its name, as it is later called ArrayOfSecondClass. Can someone help me with this?
Here is some Test-Code describing the Scenario:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
namespace TeaTimeTestEmpty
{
public class FirstClass
{
[XmlArray("RealName")] // Is ignored when SecondClassArray is passed
public SecondClass[] SecondClassArray { get; set; }
}
public class SecondClass
{
public string Name { get; set; }
}
public static class Program
{
public static string SerializeToXml(object objectToSerialize, Type objectType = null)
{
if (objectToSerialize != null)
{
if (objectType == null)
{
objectType = objectToSerialize.GetType();
}
XmlSerializer serializer = new XmlSerializer(objectType);
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, objectToSerialize, null);
string xmlString = "";
foreach (byte currentByte in stream.ToArray())
{
xmlString += (char)currentByte;
}
return xmlString;
}
}
return null;
}
static void Main(string[] args)
{
List<SecondClass> listOfSecondClasses = new List<SecondClass>();
for (int i = 0; i < 10; ++i)
{
listOfSecondClasses.Add(new SecondClass() { Name = "Bob" + i });
}
FirstClass firstClass = new FirstClass() { SecondClassArray = listOfSecondClasses.ToArray()};
// Note that I am passing only the SecondClassArray, not the whole Element
string xml = SerializeToXml(firstClass.SecondClassArray);
}
}
}
Now when I am debugging this, I get the following XML-Code in the variable xml:
<?xml version="1.0"?>
<ArrayOfSecondClass
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SecondClass>
<Name>Bob0</Name>
</SecondClass>
<SecondClass>
<Name>Bob1</Name>
</SecondClass>
<SecondClass>
<Name>Bob2</Name>
</SecondClass>
<SecondClass>
<Name>Bob3</Name>
</SecondClass>
<SecondClass>
<Name>Bob4</Name>
</SecondClass>
<SecondClass>
<Name>Bob5</Name>
</SecondClass>
<SecondClass>
<Name>Bob6</Name>
</SecondClass>
<SecondClass>
<Name>Bob7</Name>
</SecondClass>
<SecondClass>
<Name>Bob8</Name>
</SecondClass>
<SecondClass>
<Name>Bob9</Name>
</SecondClass>
</ArrayOfSecondClass>
My problem now is, that the Name I gave it in FirstClass is lost, and I cant seem to find a way to get it back, not even if I am giving the SecondClass XmlRoot or other tags, it is always calling it ArrayOfSecondClass instead of the wanted name.
I would appreciate it if you could give me a solution to how to give it the name I want it to have.
static void Main(string[] args)
{
List<SecondClass> listOfSecondClasses = new List<SecondClass>();
for (int i = 0; i < 10; ++i)
{
listOfSecondClasses.Add(new SecondClass() { Name = "Bob" + i });
}
FirstClass firstClass = new FirstClass() { SecondClassArray = listOfSecondClasses.ToArray() };
// Note that I am passing only the SecondClassArray, not the whole Element
string xml = SerializeToXml(firstClass.SecondClassArray,"RealNames");
Console.WriteLine(xml);
Console.ReadLine();
}
public static string SerializeToXml<T>(T objectToSerialize, string RootNodeName)
{
if (objectToSerialize != null)
{
XmlRootAttribute root = new XmlRootAttribute(RootNodeName);
XmlSerializer serializer = new XmlSerializer(typeof(T),root);
using (MemoryStream stream = new MemoryStream())
{
serializer.Serialize(stream, objectToSerialize, null);
string xmlString = "";
foreach (byte currentByte in stream.ToArray())
{
xmlString += (char)currentByte;
}
return xmlString;
}
}
return null;
}
The XmlArray attribute is part of FirstClass not of its property, so it is clear that it is not used when the serializer never sees an instance of FirstClass.
You will need to work with another constructor of the XmlSerializer class.
This constructor allows you to pass the name of the root element:
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "RealName";
XmlSerializer serializer = new XmlSerializer(objectType, xRoot);
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);
}
}
I have got a class named WebserviceType I got from the tool xsd.exe from an XSD file.
Now I want to deserialize an instance of an WebServiceType object to a string.
How can I do this?
The MethodCheckType object has as params a WebServiceType array.
My first try was like I serialized it: with a XmlSerializer and a StringWriter (while serializing I used a StringReader).
This is the method in which I serialize the WebServiceType object:
XmlSerializer serializer = new XmlSerializer(typeof(MethodCheckType));
MethodCheckType output = null;
StringReader reader = null;
// catch global exception, logg it and throw it
try
{
reader = new StringReader(path);
output = (MethodCheckType)serializer.Deserialize(reader);
}
catch (Exception)
{
throw;
}
finally
{
reader.Dispose();
}
return output.WebService;
Edit:
Maybe I could say it in different words: I have got an instance of this MethodCheckType object an on the other hand I have got the XML document from which I serialized this object. Now I want to convert this instance into a XML document in form of a string. After this I have to proof if both strings (of XML documents) are the same. This I have to do, because I make unit tests of the first method in which I read an XML document into a StringReader and serialize it into a MethodCheckType object.
Here are conversion method for both ways.
this = instance of your class
public string ToXML()
{
using(var stringwriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(this.GetType());
serializer.Serialize(stringwriter, this);
return stringwriter.ToString();
}
}
public static YourClass LoadFromXMLString(string xmlText)
{
using(var stringReader = new System.IO.StringReader(xmlText))
{
var serializer = new XmlSerializer(typeof(YourClass ));
return serializer.Deserialize(stringReader) as YourClass ;
}
}
I realize this is a very old post, but after looking at L.B's response I thought about how I could improve upon the accepted answer and make it generic for my own application. Here's what I came up with:
public static string Serialize<T>(T dataToSerialize)
{
try
{
var stringwriter = new System.IO.StringWriter();
var serializer = new XmlSerializer(typeof(T));
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
catch
{
throw;
}
}
public static T Deserialize<T>(string xmlText)
{
try
{
var stringReader = new System.IO.StringReader(xmlText);
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
catch
{
throw;
}
}
These methods can now be placed in a static helper class, which means no code duplication to every class that needs to be serialized.
public static string Serialize(object dataToSerialize)
{
if(dataToSerialize==null) return null;
using (StringWriter stringwriter = new System.IO.StringWriter())
{
var serializer = new XmlSerializer(dataToSerialize.GetType());
serializer.Serialize(stringwriter, dataToSerialize);
return stringwriter.ToString();
}
}
public static T Deserialize<T>(string xmlText)
{
if(String.IsNullOrWhiteSpace(xmlText)) return default(T);
using (StringReader stringReader = new System.IO.StringReader(xmlText))
{
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(stringReader);
}
}
public static class XMLHelper
{
/// <summary>
/// Usage: var xmlString = XMLHelper.Serialize<MyObject>(value);
/// </summary>
/// <typeparam name="T">Kiểu dữ liệu</typeparam>
/// <param name="value">giá trị</param>
/// <param name="omitXmlDeclaration">bỏ qua declare</param>
/// <param name="removeEncodingDeclaration">xóa encode declare</param>
/// <returns>xml string</returns>
public static string Serialize<T>(T value, bool omitXmlDeclaration = false, bool omitEncodingDeclaration = true)
{
if (value == null)
{
return string.Empty;
}
try
{
var xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = omitXmlDeclaration, //true: remove <?xml version="1.0" encoding="utf-8"?>
Encoding = Encoding.UTF8,
NewLineChars = "", // remove \r\n
};
var xmlserializer = new XmlSerializer(typeof(T));
using (var memoryStream = new MemoryStream())
{
using (var xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings))
{
xmlserializer.Serialize(xmlWriter, value);
//return stringWriter.ToString();
}
memoryStream.Position = 0;
using (var sr = new StreamReader(memoryStream))
{
var pureResult = sr.ReadToEnd();
var resultAfterOmitEncoding = ReplaceFirst(pureResult, " encoding=\"utf-8\"", "");
if (omitEncodingDeclaration)
return resultAfterOmitEncoding;
return pureResult;
}
}
}
catch (Exception ex)
{
throw new Exception("XMLSerialize error: ", ex);
}
}
private static string ReplaceFirst(string text, string search, string replace)
{
int pos = text.IndexOf(search);
if (pos < 0)
{
return text;
}
return text.Substring(0, pos) + replace + text.Substring(pos + search.Length);
}
}
This is my solution, for any list object you can use this code for convert to xml layout. KeyFather is your principal tag and KeySon is where start your Forech.
public string BuildXml<T>(ICollection<T> anyObject, string keyFather, string keySon)
{
var settings = new XmlWriterSettings
{
Indent = true
};
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
StringBuilder builder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(builder, settings))
{
writer.WriteStartDocument();
writer.WriteStartElement(keyFather);
foreach (var objeto in anyObject)
{
writer.WriteStartElement(keySon);
foreach (PropertyDescriptor item in props)
{
writer.WriteStartElement(item.DisplayName);
writer.WriteString(props[item.DisplayName].GetValue(objeto).ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
}
writer.WriteFullEndElement();
writer.WriteEndDocument();
writer.Flush();
return builder.ToString();
}
}
using dotnet 2.0. Code to illustrate :
Class1 c1 = new Class1();
c1.SomeInt = 5;
XmlDocument doc = new XmlDocument();
doc.LoadXml("<anode xmlns=\"xyz\" ><id>123</id></anode>");
c1.Any = new XmlElement[1];
c1.Any[0] = (XmlElement)doc.DocumentElement;
XmlSerializer ser = new XmlSerializer(typeof(Class1));
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "xyz");
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
XmlWriter writer = XmlWriter.Create(sb, settings);
writer.WriteStartElement("root");
ser.Serialize(writer, c1, ns);
writer.WriteEndElement();
writer.Close();
string str = sb.ToString();
MessageBox.Show(str);
where Class1 is defined like :
[System.Serializable()]
[System.Xml.Serialization.XmlRoot(Namespace="xyz")]
public class Class1
{
private int someInt;
public int SomeInt
{
get { return someInt; }
set { someInt = value; }
}
private System.Xml.XmlElement[] anyField;
/// <remarks/>
[System.Xml.Serialization.XmlAnyElementAttribute()]
public System.Xml.XmlElement[] Any
{
get
{
return this.anyField;
}
set
{
this.anyField = value;
}
}
}
This code displays the string :
<root><Class1 xmlns="xyz"><SomeInt>5</SomeInt><anode xmlns="xyz"><id>123</id></anode></Class1></root>
This is the correct xml, but I'm wondering if this can be simplified.
What I would like is to not have the redundant xmlns="xyz" part in the "anode" element.
i.e. I would like :
<root><Class1 xmlns="xyz"><SomeInt>5</SomeInt><anode><id>123</id></anode></Class1></root>
Is this possible ?
No, I don't believe you can. You could use an aliased namespace as described in this article: Prettification of XML Serialization within Web Services.
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates