Deserialize xml into a list - c#

I have a normal list based in model like:
Model:
public class ProjectHistoryModel
{
public int JobNumber { get; set; }
public int DesignKey { get; set; }
public string ProjectName { get; set; }
}
In class I have a list of this model like:
public List<ProjectHistoryModel> ProjectHistoryModel = new List<ProjectHistoryModel>();
Then I save that list into xml file as:
Serialize list:
public static string SerializeObject<T>(this T value)
{
if (value == null)
{
return string.Empty;
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringWriter = new StringWriter();
using (var writer = XmlWriter.Create(stringWriter))
{
xmlserializer.Serialize(writer, value);
return stringWriter.ToString();
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
So I save list just sending it to that method as:
var historyXml = ProjectHistoryModel.SerializeObject();
XML.HistoryProjects = historyXml;
XML.SaveXML();
Now my question is: How can I deserialize this xml and convert to a list again?
I try it something like this but I get stuck:
public static List<string> Load()
{
var xmlList = XML.HistoryProjects;
using (var stream = System.IO.File.OpenRead(FileName))
{
var serializer = new XmlSerializer(xmlList));
return serializer.Deserialize(stream) as [xmlList];
}
}
Regards

You just need to do the same thing in reverse, using a StringReader instead of a writer.
public static T DeserializeObject<T>(this string source)
{
if (string.IsNullOrEmpty(source))
{
return default(T);
}
try
{
var xmlserializer = new XmlSerializer(typeof(T));
var stringReader = new StringReader(source);
using (var reader = XmlReader.Create(stringReader))
{
var result = xmlserializer.Deserialize(reader);
return (T)result;
}
}
catch (Exception ex)
{
throw new Exception("An error occurred", ex);
}
}
Then call it with:
var input = new List<ProjectHistoryModel>();
var serialized = input.SerializeObject();
var output = serialized.DeserializeObject<List<ProjectHistoryModel>>();
Here is a link to a working example on DotNetFiddle.

Related

Issue with Deserialization with xml

I am getting error with deserialization. It says error in xml document.
public T XmlDeserialiseResponseObject<T>(string xml)
{
T returnedXmlClass;
try
{
xml = XmlResponseObjectCleaner(xml);
var doc = new XmlDocument();
doc.LoadXml(xml);
var reader = new XmlNodeReader(doc.DocumentElement);
var ser = new XmlSerializer(typeof(T));
returnedXmlClass = (T)ser.Deserialize(reader);
}
catch (Exception ex)
{
throw ex;
}
return returnedXmlClass;
}
My XML :
<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/elope/'><soap:Body><GetBenefitStatusFault xmlns:ns2='http://schemas.somesite.co.za/somesite/some/' schemaVersion='1.0'>
<ErrorCode>3513</ErrorCode>
<ErrorMessage>Membership details not valid: Match on initial not found</ErrorMessage>
</GetBenefitStatusFault></soap:Body></soap:Envelope>
XmlResponseObjectCleaner:
private string XmlResponseObjectCleaner(string xml)
{
var sb = new StringBuilder();
sb.Append(xml);
sb.Replace(#"""", "'");
sb.Replace("<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>", "");
sb.Replace("</env:Envelope>", "");
sb.Replace("<env:Header/>", "");
sb.Replace("<env:Body>", "");
sb.Replace("ns3:", "");
sb.Replace("ns2:", "");
sb.Replace("</env:Body>", "");
sb.Replace("env", "");
sb.Replace("T00:00:00.000+02:00", "");
sb.Replace("T00:00:00.000Z", "");
return sb.ToString();
}
You could:
load the XML into a DOM and pick out the Body element and parse the OuterXml from there
start at the envelope
Here's an approach for 2:
using System.IO;
using System.Xml.Serialization;
static class P
{
static void Main()
{
const string xml = #"<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/elope/'><soap:Body><GetBenefitStatusFault xmlns:ns2='http://schemas.somesite.co.za/somesite/some/' schemaVersion='1.0'>
<ErrorCode>3513</ErrorCode>
<ErrorMessage>Membership details not valid: Match on initial not found</ErrorMessage>
</GetBenefitStatusFault></soap:Body></soap:Envelope>";
var obj = XmlDeserialiseResponseObject<GetBenefitBody>(xml);
if (obj.GetBenefitStatusFault is Fault f)
{
System.Console.WriteLine(f.ErrorCode);
System.Console.WriteLine(f.ErrorMessage);
}
}
public static T XmlDeserialiseResponseObject<T>(string xml)
{
var ser = new XmlSerializer(typeof(Envelope<T>));
var obj = (Envelope<T>)ser.Deserialize(new StringReader(xml));
return obj.Body;
}
}
[XmlRoot("Envelope", Namespace = "http://schemas.xmlsoap.org/soap/elope/")]
public class Envelope<T>
{
public T Body { get; set; }
}
public class GetBenefitBody
{
[XmlElement(Namespace = "")]
public Fault GetBenefitStatusFault { get; set; }
}
public class Fault
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
}

How to Serialize Complex C# class to XML and send it as response from .net core API?

I have created an object(Complex object) in c# which I want to send a response from API in XML.
How can I set the value to complex nodes and Serialize it in proper XML.
Below is my code..
C# object which I want to convert
[XmlRoot]
[Serializable]
public class Response
{
private Response headerField;
private Response bodyField;
/// <remarks/>
public Response Header
{
get
{
return this.headerField;
}
set
{
this.headerField = value;
}
}
/// <remarks/>
public Response Body
{
get
{
return this.bodyField;
}
set
{
this.bodyField = value;
}
}
}
public class Header
{
public string ACCT_NUM { get; set; }
public string NIC { get; set; }
public string requestId { get; set; }
public string BankId { get; set; }
}
public class Body
{
public string STATUS { get; set; }
public Body ERROR { get; set; }
}
public class ERROR
{
public string ERRORCODE { get; set; }
/// <remarks/>
public string ERRORDESC { get; set; }
/// <remarks/>
public string ERRORTYPE { get; set; }
}
Serialize code by which I am setting values to fields and sending.
var header = new Header()
{
ACCT_NUM= arr[0].ACCT_NUM.ToString(),
BankId= arr[0].BankId.ToString(),
requestId= arr[0].requestId.ToString(),
NIC= arr[0].NIC.ToString()
};
var body = new Body()
{
STATUS = "SUCCESS"
};
using(var stream=new MemoryStream())
{
XmlSerializer xmlSer=new
XmlSerializer(typeof(Response));xmlSer.Serialize(stream,new Response()
{
Header = header,
Body = body
});
stream.Position = 0;
using(var reader = new StreamReader(stream, Encoding.UTF8))
{
string value = reader.ReadToEnd();
}
}
Please guide me here how can I achieve to Serialize the object in XML and sent it as response from API.
You can add xml serialisation support to .net core mvc. This would allow the client to request xml via setting the Accept header.
services.AddMvc(options =>
{
options.RespectBrowserAcceptHeader = true; // false by default
.OutputFormatters.Insert(0, new XmlSerializerOutputFormatter());
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("application/xml"));
});
Or you can match the response type based on a format argument in the request path;
[Route("{id}.{format}"), FormatFilter]
public async Task<IActionResult> Load(int id)
You can try this
public string SerializeToString()
{
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var serializer = new XmlSerializer(this.GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, this, emptyNamespaces);
return stream.ToString();
}
}
controller get x-www-form-urlencoded body and send request XML or JSON (filter.JSON=true for json response body)
[HttpPost("getbnprices")]
[Consumes("application/x-www-form-urlencoded")]
public ActionResult GetBnPricesXForm([FromForm] FilterListApiModel filter)
{
filter.UserName = filter.Login;
try
{
if (filter == null)
return BadRequest();
var result = _leftoversSupervisor.GetBnPrices(filter).ToList();
if (result == null) return Ok();
var Pricelist = new BnPriceListXml<BnPriceApiModel>();
Pricelist.AddRange(result);
if (!filter.JSON)
{
var xmlOut = XmlSerializerExtension.OutXmlString(Pricelist);
return new ContentResult
{
ContentType = "text/xml; charset=WINDOWS-1251", //if necessary change charset
Content = xmlOut,
StatusCode = 200
};
}
else
{
return Ok(Pricelist); // response body in json
}
}
catch (Exception ex)
{
return StatusCode(500, ex);
}
}
and method for serializing XML from object ( for clean result delete namespaces and change some chars for valid XML document)
public static string OutXmlString<T>(T items) where T : class
{
var xmlString = "";
var xmlSerializer = new XmlSerializer(items.GetType());
var xmlSb = new StringBuilder();
using (var stringWriter = new StringWriter())
{
using (var xmlWriter = XmlWriter.Create(stringWriter, new XmlWriterSettings { Indent = true }))
{
xmlSerializer.Serialize(xmlWriter, items);
xmlString = stringWriter.ToString();
}
}
string strXMLPatternNamspace = #"[\s]xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";
xmlString = Regex.Replace(xmlString, strXMLPatternNamspace, "");
xmlSb.Append(xmlString);
xmlSb.Replace("-", "-");
xmlSb.Replace(#"""", """);
xmlSb.Replace(#"'", "&apos;");
xmlSb.Replace(#"<?xml version="1.0" encoding="utf-16"?>", #"<? xml version = ""1.0"" encoding = ""windows-1251""?>");
return xmlSb.ToString();
}
example output
<?xml version="1.0" encoding="windows-1251"?>
<Pricelist>
<Product>
<TypeName>Моноблоки</TypeName>
<CategoryID>582</CategoryID>
<ItemType>23.8"</ItemType>
<ItemCode>365425</ItemCode>
<Artikul>XA24.HK7.I14.8.S2.N.U.075</Artikul>
<Vendor>Expert PC</Vendor>
<ItemName>Персональний комп`ютер-моноблок Expert PC G24h (XA24.HK7.I14.8.S2.N.U.075) White</ItemName>
<warranty>36</warranty>
<UCGFEA>8471</UCGFEA>
<barcode> </barcode>
<qty>10</qty>
<price>22265</price>
<price_RIP>28199</price_RIP>
<weight></weight>
<width></width>
<height></height>
<depth></depth>
<volume></volume>
</Product>
</Pricelist>
maybe need configure your service
public static void AddControllersWithFormatters(this IServiceCollection services)
{
//Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
services.AddControllers(options =>
{
options.Filters.Add(new ProducesAttribute("application/x-www-form-urlencoded"));
options.Filters.Add(new ProducesAttribute("application/xml"));
options.Filters.Add(new ProducesAttribute("text/xml"));
options.RespectBrowserAcceptHeader = true;
options.OutputFormatters.Add(new XmlSerializerOutputFormatterNamespace());
options.OutputFormatters.Add(new XmlSerializerOutputFormatter(new XmlWriterSettings
{
Encoding = Encoding.UTF8,
OmitXmlDeclaration = false
}));
}).AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
}).AddXmlSerializerFormatters();
//.AddXmlDataContractSerializerFormatters();
}

XmlSerializing only the base object

Currently I am trying to write a set of two classes: one (BackgroundManagerSettings) which will hold all the variables that I wish to save when the program is closed, and another (BackgroundManager) which contains methods and variables that will only be needed during the runtime.
Code:
public class BackgroundManager : BackgroundManagerSettings
{
//Example
private string _LastName;
public string LastName
{
get
{
return this._LastName;
}
set
{
this._LastName = value;
}
}
public BackgroundManager()
{
this.LastName = "Smith";
}
public static BackgroundManager Load(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
LoopAgain:
try
{
using (StreamReader Reader = new StreamReader(filename))
{
return Serializer.Deserialize(Reader) as BackgroundManager;
}
}
catch (FileNotFoundException)
{
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, new BackgroundManager() as BackgroundManagerSettings);
Writer.Close();
}
goto LoopAgain;
}
catch (InvalidOperationException)
{
File.Delete(filename);
goto LoopAgain;
}
}
public void Save(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, this as BackgroundManagerSettings);
Writer.Close();
}
}
}
public abstract class BackgroundManagerSettings
{
//Example
private string _FirstName;
[XmlElement("FirstName")]
public string FirstName
{
get
{
return this._FirstName;
}
set
{
this._FirstName = value;
}
}
public BackgroundManagerSettings()
{
this.FirstName = "Joe";
}
}
Usage:
BackgroundManager Manager = BackgroundManager.Load("temp.Xml");
Manager.Save("temp.Xml");
Output:
<?xml version="1.0" encoding="UTF-8"?>
<BackgroundManager xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FirstName>Joe</FirstName>
<LastName>Smith</LastName>
</BackgroundManager>
My current problem is that when I use the Save() Method it serializes the fields of BackgroundManager as well as the fields of BackgroundManagerSettings. How do I make it so it only serializes the fields of BackgroundManagerSettings? Any help or links to point me in the right direction would be much appreciated.
Mark all the public members of the base class that you don't want to be serialized with XmlIgnore attribute.
I would do it like this:
[DataContract]
public class BackgroundManager : BackgroundManagerSettings
{
[DataMember]
public int PropertyToSerialize { get; set; }
}
Add the DataMember decoration to all of the properties that you want included.
After some more research I found a method that works how I want it to thanks for the advice.
public static BackgroundManager Load(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManager));
LoopAgain:
try
{
using (StreamReader Reader = new StreamReader(filename))
{
return Serializer.Deserialize(Reader) as BackgroundManager;
}
}
catch (FileNotFoundException)
{
XmlSerializer BaseSerializer = new XmlSerializer(typeof(BackgroundManagerSettings));
using (StreamWriter Writer = new StreamWriter(filename))
{
BaseSerializer.Serialize(Writer, new BackgroundManager().ToBase());
Writer.Close();
}
goto LoopAgain;
}
catch (InvalidOperationException)
{
File.Delete(filename);
goto LoopAgain;
}
}
public void Save(string filename)
{
XmlSerializer Serializer = new XmlSerializer(typeof(BackgroundManagerSettings));
using (StreamWriter Writer = new StreamWriter(filename))
{
Serializer.Serialize(Writer, this.ToBase());
Writer.Close();
}
}
private dynamic ToBase()
{
var Temp = Activator.CreateInstance(typeof(BackgroundManagerSettings));
FieldInfo[] Fields = Temp.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (FieldInfo x in Fields)
{
x.SetValue(Temp, x.GetValue(this));
}
return Temp;
}

Avoid duplicated code in XML serializable classes

I have two Serializable classes with very similar code. Actually, except for the part where specific constructor is called, serialization code is identical.
Is there a way to create a common class to contain the common parts, so that specific classes (subclasses?) can implement only the constructor part? I can think of generics, factory pattern, but could not figure out how to do it.
// Fictitious classes
[Serializable]
public class FlightParameters {
public double MaxHeight { get; set; }
pulbic double MaxSpeedKmPerHour { get; set; }
public static FlightParameters Load(String fname) {
FlightParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(FlightParameters));
try {
result = (FlightParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new FlightParameters() {
MaxHeight = 30000;
MaxSpeedKmPerHour = 1500;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
[Serializable]
public class SailingParameters {
public double MaxDepth { get; set; }
pulbic double MaxSpeedKnots { get; set; }
public static SailingParameters Load(String fname) {
SailingParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(SailingParameters));
try {
result = (SailingParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new SailingParameters() {
MaxDepth = 13000;
MaxSpeedKnots = 15;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
Usage:
FlightParameters _fparam = FlightParameters.Load(somePath);
SailingParameters _sparam = SailingParameters.Load(someOtherPath);
The easiest way I can see to do that would be something like:
static class XmlUtils {
public static T Load<T>(string filename, Func<T> onMissing = null)
where T : class, new()
{
using (var fs = File.OpenRead(filename)) {
var serializer = new XmlSerializer(typeof(T));
try {
return (T)serializer.Deserialize(fs);
} catch (InvalidOperationException) { // catch "file not found"
return onMissing == null ? new T() : onMissing();
}
}
}
}
allowing something like;
public static SailingParameters Load(string filename) {
return XmlUtils.Load<SailingParameters>(filename, () => new SailingParameters {
MaxDepth = 13000;
MaxSpeedKnots = 15;
});
}

Remove namespace from DataContract doesn't work

I have to two simple serialize/desirialize methods,
Mapping:
[System.Runtime.Serialization.DataContract(Namespace = "", Name = "PARAMS")]
public sealed class CourseListRequest {
[DataMember(Name = "STUDENTID")]
public int StudentId { get; set; }
[DataMember(Name = "YEAR")]
public string Year { get; set; }
[DataMember(Name = "REQUESTTYPE")]
public int RequestType { get; set; }
}
public static string Serialize<T>(this T value) {
if (value == null) throw new ArgumentNullException("value");
try {
var dcs = new DataContractSerializer(typeof (T));
string xml;
using (var ms = new MemoryStream()) {
dcs.WriteObject(ms, value);
xml = Encoding.UTF8.GetString(ms.ToArray());
}
return xml;
}
catch (Exception e) {
throw;
}
}
public static T Deserialize<T>(this string xml) where T : class {
if (string.IsNullOrEmpty(xml)) {
return default(T);
}
try {
var dcs = new DataContractSerializer(typeof (T));
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(xml))) {
ms.Position = 0;
return dcs.ReadObject(ms) as T;
}
}
catch (Exception e) {
throw;
}
}
result:
<PARAMS xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><REQUESTTYPE>36</REQUESTTYPE><STUDENTID>0</STUDENTID><YEAR>תשע</YEAR></PARAMS>
How to remove xmlns:i="http://www.w3.org/2001/XMLSchema-instance" ?? On serializing
Switch to using XmlSerializer
System.Xml.Serialization.XmlSerializer
This will generate plain XML with no namespaces

Categories

Resources