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);
}
}
Related
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 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();
}
}
I have a class that is serialized into XML for consumption by a web service. In this classes instance the XML must include a CDATA section for the web service to read it but I am at a loss on how to implement this.
The XML needs to look like:
<UpdateOrderStatus>
<Action>2</Action>
<Value>
<![CDATA[
<Shipment>
<Header>
<SellerID>
...
]]>
</Value>
</UpdateOrderStatus>
I am able to generate the appropriate XML, except for the CDATA part.
My class structure looks like:
public class UpdateOrderStatus
{
public int Action { get; set; }
public ValueInfo Value { get; set; }
public UpdateOrderStatus()
{
Value = new ValueInfo();
}
public class ValueInfo
{
public ShipmentInfo Shipment { get; set; }
public ValueInfo()
{
Shipment = new ShipmentInfo();
}
public class ShipmentInfo
{
public PackageListInfo PackageList { get; set; }
public HeaderInfo Header { get; set; }
public ShipmentInfo()
{
PackageList = new PackageListInfo();
Header = new HeaderInfo();
}
....
I have seen some suggestions on using:
[XmlElement("node", typeof(XmlCDataSection))]
but that causes an exception
I have also tried
[XmlElement("Value" + "<![CDATA[")]
but the resulting XML is incorrect showing
<Value_x003C__x0021__x005B_CDATA_x005B_>
....
</Value_x003C__x0021__x005B_CDATA_x005B_>
Can anyone show me what I am doing wrong, or where I need to go with this?
--Edit--
making shipmentInfo serializable per carlosfigueira works for the most part, however I get extra ? characters in the resulting XML ( see post Writing an XML fragment using XmlWriterSettings and XmlSerializer is giving an extra character for details )
As such I changed the Write XML method to:
public void WriteXml(XmlWriter writer)
{
using (MemoryStream ms = new MemoryStream())
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Encoding = new UnicodeEncoding(bigEndian: false, byteOrderMark: false);
settings.Indent = true;
using (XmlWriter innerWriter = XmlWriter.Create(ms, settings))
{
shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns);
innerWriter.Flush();
writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
However I am not getting an exception:
System.InvalidOperationException: There was an error generating the XML document. ---> System.ArgumentException: '.', hexadecimal
value 0x00, is an invalid character.
--Edit --
The exception was caused by the inclusion of my previous serializeToString method. Since removing that the CDATA output is correct, except for a spacing issue, but I am also getting a namespace and xml declaration that should be removed by the XML settings specified. Output is:
<?xml version="1.0"?>
<UpdateOrderStatus xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Action>1</Action>
<Value><![CDATA[< S h i p m e n t I n f o >
< P a c k a g e L i s t >
< P a c k a g e >
< S h i p D a t e > 2 0 1 2 - 0 7 - 1 3 T 1 1 : 5 8 : 5 1 . 0 9 2 5 6 1 5 - 0 4 : 0 0 < / S h i p D a t e >
< I t e m L i s t >
< I t e m >
< S h i p p e d Q t y > 0 < / S h i p p e d Q t y >
< / I t e m >
< / I t e m L i s t >
< / P a c k a g e >
< / P a c k a g e L i s t >
< H e a d e r >
< S e l l e r I d > S h i p m e n t h e a d e r < / S e l l e r I d >
< S O N u m b e r > 0 < / S O N u m b e r >
< / H e a d e r >
< / S h i p m e n t I n f o > ]]></Value>
</UpdateOrderStatus>
Any ideas of avoiding the BOM using the new class?
--Edit 3 -- SUCCESS!
I have implemented changes suggested below and now have the following writer class and test methods:
UpdateOrderStatus obj = new UpdateOrderStatus();
obj.Action = 1;
obj.Value = new UpdateOrderStatus.ValueInfo();
obj.Value.Shipment = new UpdateOrderStatus.ValueInfo.ShipmentInfo();
obj.Value.Shipment.Header.SellerId = "Shipment header";
obj.Value.Shipment.PackageList = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo();
obj.Value.Shipment.PackageList.Package = new UpdateOrderStatus.ValueInfo.ShipmentInfo.PackageListInfo.PackageInfo();
obj.Value.Shipment.PackageList.Package.ShipDate = DateTime.Now;
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Encoding = new UTF8Encoding(false);
settings.Indent = true;
XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus));
MemoryStream ms = new MemoryStream();
XmlWriter writer = XmlWriter.Create(ms, settings);
xs.Serialize(writer, obj, ns);
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
}
public void WriteXml(XmlWriter writer)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Indent = true;
StringBuilder sb = new StringBuilder();
using (XmlWriter innerWriter = XmlWriter.Create(sb, settings))
{
shipmentInfoSerializer.Serialize(innerWriter, this.Shipment, ns);
innerWriter.Flush();
writer.WriteCData(sb.ToString());
}
}
This produces the following XML:
<UpdateOrderStatus>
<Action>1</Action>
<Value><![CDATA[<ShipmentInfo>
<PackageList>
<Package>
<ShipDate>2012-07-13T14:05:36.6170802-04:00</ShipDate>
<ItemList>
<Item>
<ShippedQty>0</ShippedQty>
</Item>
</ItemList>
</Package>
</PackageList>
<Header>
<SellerId>Shipment header</SellerId>
<SONumber>0</SONumber>
</Header>
</ShipmentInfo>]]></Value>
</UpdateOrderStatus>
In response to the 'spaces' you are seeing after your edit, it is because of the encoding you are using (Unicode, 2 bytes per character).
Try:
settings.Encoding = new Utf8Encoding(false);
EDIT:
Also, note that format of the MemoryStream is not necessarily a valid UTF-8 encoded string! You can use a StringBuilder instead of MemoryStream to create your inner writer.
public void WriteXml(XmlWriter writer)
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.Indent = true;
StringBuilder sb = new StringBuilder();
using (XmlWriter innerWriter = XmlWriter.Create(sb, settings))
{
shipmentInfoSerializer.Serialize(innerWriter, this.Shipment,ns);
innerWriter.Flush();
writer.WriteCData(sb.ToString());
}
}
Could this be of any help: http://msdn.microsoft.com/en-us/library/system.xml.xmldocument.createcdatasection.aspx
//Create a CData section.
XmlCDataSection CData;
CData = doc.CreateCDataSection("All Jane Austen novels 25% off starting 3/23!");
//Add the new node to the document.
XmlElement root = doc.DocumentElement;
root.AppendChild(CData);
Console.WriteLine("Display the modified XML...");
doc.Save(Console.Out);
Also, what Exception did you get when using the attribute?
-- edit --
You could try adding a custom class, and do something like this:
some xml serializable class,
{
.......
[XmlElement("PayLoad", Type=typeof(CDATA))]
public CDATA PayLoad
{
get { return _payLoad; }
set { _payLoad = value; }
}
}
public class CDATA : IXmlSerializable
{
private string text;
public CDATA()
{}
public CDATA(string text)
{
this.text = text;
}
public string Text
{
get { return text; }
}
/// <summary>
/// Interface implementation not used here.
/// </summary>
XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
/// <summary>
/// Interface implementation, which reads the content of the CDATA tag
/// </summary>
void IXmlSerializable.ReadXml(XmlReader reader)
{
this.text = reader.ReadElementString();
}
/// <summary>
/// Interface implementation, which writes the CDATA tag to the xml
/// </summary>
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteCData(this.text);
}
}
As found here http://bytes.com/topic/net/answers/530724-cdata-xmltextattribute
Implementing ShipmentInfo as an IXmlSerializable type will get close to what you need - see example below.
public class StackOverflow_11471676
{
public class UpdateOrderStatus
{
public int Action { get; set; }
public ValueInfo Value { get; set; }
}
[XmlType(TypeName = "Shipment")]
public class ShipmentInfo
{
public string Header { get; set; }
public string Body { get; set; }
}
public class ValueInfo : IXmlSerializable
{
public ShipmentInfo Shipment { get; set; }
private XmlSerializer shipmentInfoSerializer = new XmlSerializer(typeof(ShipmentInfo));
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
using (MemoryStream ms = new MemoryStream(
Encoding.UTF8.GetBytes(
reader.ReadContentAsString())))
{
Shipment = (ShipmentInfo)this.shipmentInfoSerializer.Deserialize(ms);
}
}
public void WriteXml(XmlWriter writer)
{
using (MemoryStream ms = new MemoryStream())
{
using (XmlWriter innerWriter = XmlWriter.Create(ms, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
shipmentInfoSerializer.Serialize(innerWriter, this.Shipment);
innerWriter.Flush();
writer.WriteCData(Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
}
public static void Test()
{
UpdateOrderStatus obj = new UpdateOrderStatus
{
Action = 1,
Value = new ValueInfo
{
Shipment = new ShipmentInfo
{
Header = "Shipment header",
Body = "Shipment body"
}
}
};
XmlSerializer xs = new XmlSerializer(typeof(UpdateOrderStatus));
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, obj);
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
}
}
The below example is only when the structure of the schema is defined and you have no choice of altering the schema.
When you Deserialize/Serialize a [xmltext] value it is very difficult to hold the text in the CDATA[]
enclose. you can use compiletransform to get the CDATA value in the xml as it is but the CDATA is lost as soon as you deserialize in C# and load to memory stuff.
This is one of the easiest way to do
deserialize/Serialize
once the final xml output is derived. The final xml can be
converted to string and parsed as shown below and return it as
string will embed CDATA to the test1 value.
string xml ="<test><test1>###!#!#!##!##%$%##$%#$%</test1></test>";
XNamespace ns = #"";
XDocument doc = XDocument.Parse(xml);
string xmlString = string.Empty;
var coll = from query in doc.Descendants(ns + "test1")
select query;
foreach (var value in coll){
value.ReplaceNodes(new XCData(value .Value));
}
doc.save("test.xml");// convert doc.tostring()
I think carlosfigueira just gave an elegant answer, but perhaps a little hard to understand at first sight. Here is an alternative for your consideration, you can Serialize/Deserialize UpdateOrderStatus and ShipmentInfo separately.
Define your business object class as:
[XmlRoot("UpdateOrderStatus")]
public class UpdateOrderStatus
{
[XmlElement("Action")]
public int Action { get; set; }
private String valueField;
[XmlElement("Value")]
public XmlCDataSection Value
{
get
{
XmlDocument xmlDoc = new XmlDocument();
return xmlDoc.CreateCDataSection(valueField);
}
set
{
this.valueField = value.Value;
}
}
[XmlIgnore]
public ShipmentInfo Shipment
{
get;
set;
}
}
[XmlRoot("ShipmentInfo")]
public class ShipmentInfo
{
[XmlElement("Package")]
public String Package { get; set; }
[XmlElement("Header")]
public String Header { get; set; }
}
Note that you should use XmlCDataSection for the Value field.
Here is the test/helper functions:
// Test function
const string t = #"<UpdateOrderStatus>
<Action>2</Action>
<Value>
<![CDATA[<ShipmentInfo>
<Package>packageInfo</Package>
<Header>headerInfo</Header>
</ShipmentInfo>]]>
</Value>
</UpdateOrderStatus>";
static void Test1()
{
UpdateOrderStatus os = Deserialize(t);
String t2 = XmlUtil.Serialize(os);
}
// Helper functions
static UpdateOrderStatus Deserialize(String str)
{
UpdateOrderStatus os = XmlUtil.DeserializeString<UpdateOrderStatus>(str);
os.Shipment = XmlUtil.DeserializeString<ShipmentInfo>(os.Value.InnerText);
return os;
}
public static class XmlUtil
{
public static String Serialize<T>(T o)
{
XmlSerializer s = new XmlSerializer(typeof(T)); //, overrides);
//StringBuilder builder = new StringBuilder();
MemoryStream ms = new MemoryStream();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
using (XmlWriter xmlWriter = XmlWriter.Create(ms, settings))
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add(String.Empty, String.Empty);
s.Serialize(xmlWriter, o, ns);
}
Byte[] bytes = ms.ToArray();
// discard the BOM part
Int32 idx = settings.Encoding.GetPreamble().Length;
return Encoding.UTF8.GetString(bytes, idx, bytes.Length - idx);
}
public static T DeserializeString<T>(String content)
{
using (TextReader reader = new StringReader(content))
{
XmlSerializer s = new XmlSerializer(typeof(T));
return (T)s.Deserialize(reader);
}
}
...
}
I'm having a problem with the c# XML serialization system it's throws an Ambigus exception,
There was an error generating the XML document.
Now i have a Class that contains refferences to other classes and arrays of the other classes
E.G
namespace P2PFileLayout
{
public class p2pfile
{
public FileList FileList;
public StatusServer StatusServer;
public String Hash;
}
}
namespace P2PFileLayout.parts
{
public class StatusServer
{
public Auth Auth;
public Servers Servers;
}
public class Servers
{
public Server[] Server;
}
public class Server
{
public bool AuthRequired = false;
public string Address;
}
public class Files
{
public File[] File;
}
public class File
{
public string FileName = "";
public int BlockSize = 0;
public int BlockCount = 0;
}
public class Directory
{
public string Name;
public Files Files;
public Directory[] Dir;
}
public class Auth
{
public AuthServer[] AuthServer;
}
public class FileList
{
public Files Files;
public Directory[] Directory;
}
}
My Example data
// create the test file
testFile = new p2pfile();
// create a fake fileList
testFile.FileList = new P2PFileLayout.parts.FileList();
testFile.FileList.Directory = new P2PFileLayout.parts.Directory[1];
testFile.FileList.Directory[0] = new P2PFileLayout.parts.Directory();
testFile.FileList.Directory[0].Name = "testFolder";
testFile.FileList.Directory[0].Files = new P2PFileLayout.parts.Files();
testFile.FileList.Directory[0].Files.File = new P2PFileLayout.parts.File[2];
testFile.FileList.Directory[0].Files.File[0] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[0].FileName = "test.txt";
testFile.FileList.Directory[0].Files.File[0].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[0].BlockCount = 1;
testFile.FileList.Directory[0].Files.File[1] = new P2PFileLayout.parts.File();
testFile.FileList.Directory[0].Files.File[1].FileName = "test2.txt";
testFile.FileList.Directory[0].Files.File[1].BlockSize = 64;
testFile.FileList.Directory[0].Files.File[1].BlockCount = 1;
// create a fake status server
testFile.StatusServer = new P2PFileLayout.parts.StatusServer();
testFile.StatusServer.Servers = new P2PFileLayout.parts.Servers();
testFile.StatusServer.Servers.Server = new P2PFileLayout.parts.Server[1];
testFile.StatusServer.Servers.Server[0] = new P2PFileLayout.parts.Server();
testFile.StatusServer.Servers.Server[0].Address = "http://localhost:8088/list.php";
// create a file hash (real)
HashGenerator.P2PHash hashGen = new HashGenerator.P2PHash();
testFile.Hash = hashGen.getHash();
treeView1.Nodes.Add(new TreeNode("Loading..."));
Classes.CreateTreeView ctv = new Classes.CreateTreeView();
ctv.BuildTreeView(testFile.FileList, treeView1);
treeView1.AfterCheck += new TreeViewEventHandler(treeView1_AfterCheck);
Now that is not as complicated as mine in terms of dept as my i loop objects so dir has support for more dirs but thats just an example
Then i'm serializing to a string var as i want to do a little more than just serialize it but here is my serialization
private string ToXml(object Obj, System.Type ObjType)
{
// instansiate the xml serializer object
XmlSerializer ser = new XmlSerializer(ObjType);
// create a memory stream for XMLTextWriter to use
MemoryStream memStream = new MemoryStream();
// create an XML writer using our memory stream
XmlTextWriter xmlWriter = new XmlTextWriter(memStream, Encoding.UTF8);
// write the object though the XML serializer method using the W3C namespaces
ser.Serialize(xmlWriter, Obj);
// close the XMLWriter
xmlWriter.Close();
// close the memoryStream
memStream.Close();
// get the string from the memory Stream
string xml = Encoding.UTF8.GetString(memStream.GetBuffer());
// remove the stuff before the xml code we care about
xml = xml.Substring(xml.IndexOf(Convert.ToChar(60)));
// clear any thing at the end of the elements we care about
xml = xml.Substring(0, (xml.LastIndexOf(Convert.ToChar(62)) + 1));
// return the XML string
return xml;
}
Can any one see why this is not working or any clues as to why it would not work normally
Why are you doing it manually?
What about this approach?
http://support.microsoft.com/kb/815813
Test test = new Test() { Test1 = "1", Test2 = "3" };
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(test.GetType());
MemoryStream ms = new MemoryStream();
x.Serialize(ms, test);
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
string xml = sr.ReadToEnd();
As for the ambigous message, take a look into the inner exceptions. XML serialization errors use innerexception a lot, and you usually have to look to all levels of InnerExceptions to know what is really happening.
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" />