I have a class with fields - Id , name, company and another 29 fields.
public class Employee
{
[XmlAttribute("textbox1")]
public int Id { get; set; }
[XmlAttribute("textbox11")]
public string Name { get; set; }
[XmlAttribute("textbox21")]
public string Company { get; set; }
[XmlAttribute("textbox22")]
public bool Val1 { get; set; }
[XmlAttribute("textbox23")]
public bool Val2 { get; set; }
public bool TestSomething{get;set;}
}
public class ParseData()
{
List<Employee> employee = new List<Employee>();
XmlSerialiser serializer = new XmlSerializer();
using(FileStream stream = new FileStream("test.xml", FileMode.Open))
{
employees = (Employee)serializer.Deserialize(stream);
}
//Todo: Add the new calculated value TestSomething
}
I'm deserialising the values from the xml data ( which has around 100 employees).
Now I have a new field 'TestSomething' which is not deserialised but will be result of deserialised value as shown below. Is there a way I could add calculated values to the existing list employees ?
foreach(var i in employees)
{
employee.TestSomething = (Val1 == true) ? Val1 : Val2;
}
Any suggestion on this will be appreciated.
The code snippets below are extension methods I use to serialize and deserialize xml strings. Note these need to be put in a static class and referenced in the class for use.
/// <summary>
/// Serializes the object to xml string.
/// </summary>
/// <returns>xml serialization.</returns>
public static string SerializeToXml<T>(this T instance)
where T : class, new()
{
string result;
var format = new XmlSerializer(typeof(T));
using (var strmr = new StringWriter())
{
format.Serialize(strmr, instance);
result = strmr.ToString();
}
return result;
}
/// <summary>
/// Deserializes xml serialized objects.
/// </summary>
/// <param name="xmlDocument">serialized string.</param>
/// <param name="result">out parameter.</param>
/// <returns>Instance object.</returns>
public static bool TryParseXml<T>(this string xmlDocument, out T result)
where T : class, new()
{
result = null;
if (string.IsNullOrWhiteSpace(xmlDocument))
{
return false;
}
try
{
using (TextReader str = new StringReader(xmlDocument))
{
var format = new XmlSerializer(typeof(T));
XmlReader xmlReader = XmlReader.Create(str);
if (format.CanDeserialize(xmlReader))
{
result = format.Deserialize(xmlReader) as T;
}
}
}
catch (InvalidOperationException)
{
return false;
}
return !(result is null);
}
In practice you would have a class of some type that has properties that you would like to store to a disk or database etc. Lets say I have a class:
public class MyPoint
{
public double X { get; set;}
public double Y { get; set;}
}
If I wanted to store a list of MyPoint without much heavy lifting I could serialize the class directly using the SerializeToXml method above.
var myPoints = new List<MyPoint>{new MyPoint{ X= 1, Y = 2}, new MyPoint{X = 3, Y = 4}};
var xmlString = myPoints.SerializeToXml();
Everything is fine and dandy, my info is saved as xml in some file somewhere. But then my boss says, "Dang, sorry about this but we need a Z value now that is computed from the X and Y of each using Z = X * X + Y * Y."
Adding properties to the serialized class isn't a problem (taking properties out is!!
The serializer will try to assign a value to a property that doesn't exist and throw an exception.). But in this case we don't need to worry. I go to the original MyPoint class and add a Z.
public class MyPoint
{
public double X { get; set;}
public double Y { get; set;}
public double Z { get; set;} // <---- Here
}
Now when I parse the xml I will get classes with the new property. Using the TryParseXml I get my list of points.
// assume I have a string already from the xml document to use.
List<MyPoint> points = null;
if(serializedPoints.TryParse(out points))
{
foreach(var point in points)
{
point.Z = X * X + Y * Y;
}
}
var serializedPointsWithZ = points.SerializeToXml();
Now serializedPointsWithZ is just a string that can be written to a file etc.
Related
Say I have some classes that I often serialize normally, such as
public class A
{
public A(int x, bool y)
{
X = x;
Y = y;
}
[JsonProperty("x_thing")]
public int X { get; }
[JsonProperty("y_thing")]
public bool Y { get; }
}
public class B
{
public B(string s)
{
S = s;
}
[JsonProperty("string_thing")]
public string S { get; }
}
If I want to start here (but pretend A and B are arbitrary objects):
var obj1 = new A(4, true);
var obj2 = new B("hello world");
... then how can I idiomatically produce this JSON serialization?
{
"x_thing": 4,
"y_thing": true,
"string_thing": "hello world"
}
JObject has a Merge method:
var json1 = JObject.FromObject(obj1);
var json2 = JObject.FromObject(obj2);
json1.Merge(json2);
// json1 now contains the desired result
fiddle
If your objects contain properties with the same name, you can use the overload taking a JsonMergeSettings object to specify how conflicts should be resolved.
I have application, which must contain map. To realize this task I create some classes:
1. Map (contains Image and two objects of Location class)
2. GPSPoint (contains two objects of ICoordinate)
3. ImagePoint (contains two int variables)
4. Location (contains GPSPoint and ImagePoint)
And one interface, and two classes, which realize it:
1. ICoordinate
2. GpsCoordinateDeg
3. GpsCoordinateDegMinSec
All of them implements ISerialization interface and have public void GetObjectData(SerializationInfo, StreamingContext) methods.
I want to save my map in the file, and I realize one of the methods of serialization, but it isn't work - I get void xml file:
<?xml version="1.0"?>
<Map xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
I use my class for serialization in this code:
[Serializable]
class Map : ISerializable {
...
public void saveInFile(string filepath) {
Serializer serializer = new Serializer();
serializer.SerializeObject(this, filepath);
}
...
}
This is code of my Serializer:
class Serializer {
public void SerializeObject<T>(T serializableObject, string fileName) {
if (serializableObject == null) { return; }
try {
XmlDocument xmlDocument = new XmlDocument();
XmlSerializer serializer = new XmlSerializer(serializableObject.GetType());
using (MemoryStream stream = new MemoryStream()) {
serializer.Serialize(stream, serializableObject);
stream.Position = 0;
xmlDocument.Load(stream);
xmlDocument.Save(fileName);
stream.Close();
}
} catch (Exception ex) {
//Log exception here
}
}
public T DeSerializeObject<T>(string fileName) {
if (string.IsNullOrEmpty(fileName)) { return default(T); }
T objectOut = default(T);
try {
string attributeXml = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(fileName);
string xmlString = xmlDocument.OuterXml;
using (StringReader read = new StringReader(xmlString)) {
Type outType = typeof(T);
XmlSerializer serializer = new XmlSerializer(outType);
using (XmlReader reader = new XmlTextReader(read)) {
objectOut = (T)serializer.Deserialize(reader);
reader.Close();
}
read.Close();
}
} catch (Exception ex) {
//Log exception here
}
return objectOut;
}
}
Where is the problem?
As i dont know the complete implementation of your poco classes i suggest you to look further to your GPSPoint class:
GPSPoint (contains two objects of ICoordinate)
You can not serialize an interface. The problem is that an interface is an opaque type. There is no way for the serializer to know what to write out and more importantly what to create when it needs to serialize things back.
You may look into StackOverflow for "serializing interfaces"-postings. I hope it helps.
Bad idea going for generics with serialization since it heavily relies on object boxing and unboxing.
My way of doing this does not rely on implementing ISerializable, rather on the careful use of attributes.
Decorate each class below with [Serializable()], [XmlType(AnonymousType=true)], [XmlRoot(Namespace="", IsNullable=false)]. The enum stands for coordinate types. Derive your existing classes from these ones. Mark properties you do not want serialized with [XmlIgnore] in your derived classes. Make Coordinate below implement your ICoordinate.
public partial class Location {
[XmlElement]
public GPSPoint GPSPoint { get; set; }
[XmlElement]
public ImagePoint ImagePoint { get; set; }
}
public partial class GPSPoint {
[XmlElement(ElementName = "Coordinate", Order = 0)]
public Coordinate Coordinate1 {get; set; }
[XmlElement(Order=1, ElementName="Coordinate")]
public Coordinate Coordinate2 {get;set;}
}
public partial class Coordinate {
[XmlAttribute()]
public ICoordinateType type {get;set;}
}
[Serializable]
public enum ICoordinateType {
/// <remarks/>
GpsCoordinateDegMinSec,
/// <remarks/>
GpsCoordinateDeg,
}
public partial class Map {
[XmlElement(Order=0, IsNullable=true)]
public object Image { get; set; }
/// <remarks/>
[XmlElement(ElementName="Location", Order = 1)]
public Location Location1 {get; set; }
/// <remarks/>
[XmlElement(ElementName = "Location", Order = 2)]
public Location Location2 {get; set; }
}
This is tested and running:
var tc1 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDeg};
var tc2 = new xyz.Coordinate () {type = xyz.ICoordinateType.GpsCoordinateDegMinSec};
var l1 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() { Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 0, y = 0 } };
xyz.Map m = new xyz.Map() {
Image = null,
Location1 = l1,
Location2 = new xyz.Location() {
GPSPoint = new xyz.GPSPoint() {
Coordinate1 = tc1, Coordinate2 = tc2 },
ImagePoint = new xyz.ImagePoint() { x = 1, y = 2 }
} };
XmlSerializer xs = new XmlSerializer(typeof(Map));
using (var fs = File.Create("map.xml") ) { xs.Serialize(fs, m); }
Data contains place_id and other columns:
place_id name city address ..
133 place1 Laagri Born 12 ..
161 place2 Mourdi Karve 12 ..
Data is avaliable in 5 different formats. All those urls return same data:
http://www.smartpost.ee/places.html
http://www.smartpost.ee/places.xml
http://www.smartpost.ee/places.csv
http://www.smartpost.ee/places.js
http://www.smartpost.ee/places.php
One of urls should selected to get data. Data may change so it should not cached.
How to issue http GET requet and create two element List: first element is place_id from first column and second element is concatenated name, city and address field. Something like:
class DeliveryList {
public string Id, Address;
}
List<DeliveryList> res= GetAndParse("http://www.smartpost.ee/places.csv",
"place_id, name+\" \"+ city +\" \" + address" );
How to implement GetAndParse ? Should http request saved to sting and filehelpers used to parse it ? Expression "place_id, name+\" \"+ city +\" \" + address" can hard-coded in code instead of passing as parameter.
Using ASP.NET MVC4, .NET 4, C# . Code should run in MVC4 controller in server.
I suggest you to use XML endpoint and implement deserialization to array of places as implemented below:
public static class PlacesHelper
{
private const string EndpointUrl = "http://www.smartpost.ee/places.xml";
/// <summary> Load array of places </summary>
public static async Task<Places> LoadPlacesAsync()
{
var xmlString = await HttpRequestHelper.DownloadStringAsync(EndpointUrl);
return SerializationHelper.DeserializeXmlString<Places>(xmlString);
}
}
public static class SerializationHelper
{
/// <summary> Deserializes Xml string of type T. </summary>
public static T DeserializeXmlString<T>(string xmlString)
{
T tempObject = default(T);
using (var memoryStream = new MemoryStream(StringToUTF8ByteArray(xmlString)))
{
var xs = new XmlSerializer(typeof (T));
var xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
tempObject = (T) xs.Deserialize(memoryStream);
}
return tempObject;
}
/// <summary> Convert String to Array </summary>
private static Byte[] StringToUTF8ByteArray(String xmlString)
{
return new UTF8Encoding().GetBytes(xmlString);
}
}
public static class HttpRequestHelper
{
/// <summary> Download String Async </summary>
public static async Task<string> DownloadStringAsync(string uri)
{
var client = new WebClient();
return await client.DownloadStringTaskAsync(uri);
}
}
[Serializable]
[XmlRoot("places_info", Namespace = "")]
public class Places
{
[XmlElement("place", typeof(Place), Form = XmlSchemaForm.Unqualified, IsNullable = false)]
public Place[] Place { get; set; }
}
[Serializable]
public class Place
{
[XmlElement("place_id")]
public string Id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("city")]
public string City { get; set; }
[XmlElement("address")]
public string Address { get; set; }
}
Usage:
var arrayOfPlaces = await PlacesHelper.LoadPlacesAsync();
So, you will get an array of places (id, name, city, address).
UPDATE:
For .NET Framework 4 Microsoft released the Async Targeting Pack (Microsoft.Bcl.Async) through Nuget. So Install the package 'Microsoft Async' from Nuget and you will be able to use async/await.
Example implementation of controller action:
public class TestController : Controller
{
public async Task<object> GetPlaces()
{
return Json(await PlacesHelper.LoadPlacesAsync(), JsonRequestBehavior.AllowGet);
}
}
I choose XML format to parse data, using XmlSerializer. Here is the code.
Classes represent our XML data: the code is self-explained
[XmlRoot("places_info")]
public class PlacesInfo
{
[XmlElement("place")]
public Place[] Places { get; set; }
}
public class Place
{
[XmlElement("place_id")]
public string Id { get; set; }
[XmlElement("name")]
public string Name { get; set; }
[XmlElement("city")]
public string City { get; set; }
[XmlElement("address")]
public string Address { get; set; }
}
Your Delivery class:
public class Delivery
{
public string Id { get; set; }
public string Address { get; set; }
public Delivery(string id, string address)
{
Id = id;
Address = address;
}
}
Code to get and parse data:
static void Main(string[] args)
{
// Get XML string from web
var client = new WebClient();
// Set encoding for non-ASCII characters
client.Encoding = Encoding.UTF8;
string xml = client.DownloadString("http://www.smartpost.ee/places.xml");
// Create a serializer
var serializer = new XmlSerializer(typeof(PlacesInfo));
// Variable to hold all XML data
PlacesInfo info;
// Deserialize XML string to object
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
info = (PlacesInfo) serializer.Deserialize(stream);
}
// Create delivery list
var deliveryList = new List<Delivery>();
// For each place
foreach (var place in info.Places)
{
// Concatenate name, city, and address
string address = string.Format("{0} {1} {2}", place.Name, place.City, place.Address);
// Add new delivery
var delivery = new Delivery(place.Id, address);
deliveryList.Add(delivery);
// Display to test
Console.WriteLine(delivery.Id + " " + delivery.Address);
}
Console.ReadLine();
}
I put comment in the code carefully. If you find something hard to understand, feel free to let me know. Hope this help :)
I'm trying to save a dictionary of Matrix into an Xml file.
My Matrix class attributes are :
public class Matrix
{
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string name { get; set; }
}
After many attempts, I wrote this :
string fileName = dlg.FileName;
Stream writer = new FileStream(fileName,FileMode.Create);
foreach (KeyValuePair<String, Matrix> matrice in CalMat.Calculatrice.listMatrix)
{
XmlSerializer x = new XmlSerializer(matrice.GetType());
x.Serialize(writer, matrice);
}
writer.Close();
If i run this code with one matrix, the file is created, but i only have this sentence written :
<?xml version="1.0"?><KeyValuePairOfStringMatrix xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" /><?xml version="1.0"?>
I think my code is missing something but I don't know what. A write method, I guess.
Thank you for your time!
I don't think the default KeyValuePair is serializable,
try building your own KeyValuePair class:
[Serializable]
[XmlType(TypeName="MyTypeName")]
public struct KeyValuePair<T1, T2>
{
public T1 Key { get; set; }
public T2 Value { get; set; }
}
Using BinaryFormatter this is the code:
[Serializable] // mark with Serializable
public class Matrix
{
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Lines, Columns];
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[,] Elements { get; set; }
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(stream, listMatrix);
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (Dictionary<string, Matrix>)binaryFormatter.Deserialize(stream);
stream.Close();
}
Using XmlSerializer this is the code:
// I implement my custom KeyValuePair to serialize (because XmlSerializer can not serialize the .net KeyValuePair)
public struct CustomKeyValuePair<T1, T2>
{
public CustomKeyValuePair(T1 key, T2 value): this()
{
Key = key;
Value = value;
}
public T1 Key { get; set; }
public T2 Value { get; set; }
// here I specify how is the cast
public static explicit operator CustomKeyValuePair<T1, T2>(KeyValuePair<T1, T2> keyValuePair)
{
return new CustomKeyValuePair<T1, T2>(keyValuePair.Key, keyValuePair.Value);
}
}
// Matrix class used to Serialize with XmlSerailzer
public class Matrix
{
public Matrix() { } // need a default constructor
public Matrix(string name, int lines, int columns)
{
Name = name;
Lines = lines;
Columns = columns;
Elements = new double[Columns][];
for (int i = 0; i < Elements.Length; i++)
{
Elements[i] = new double[Columns];
}
}
public int Lines { get; set; }
public int Columns { get; set; }
public double[][] Elements { get; set; } // I use double[][] because XmlSerialzer can not serialize a two-dimensional array (double[,])
public string Name { get; set; }
}
public static void Main()
{
var path = #"D:\serialize.data"; // use the path that you want
// this is an example collection
var listMatrix = new Dictionary<string, Matrix>();
listMatrix.Add("matrix_1", new Matrix("Matrix 1", 1, 2));
listMatrix.Add("matrix_2", new Matrix("Matrix 2", 2, 2));
// Serialization
var stream = new FileStream(path, FileMode.Create);
var xmlSerializer = new XmlSerializer(typeof(CustomKeyValuePair<string, Matrix>[]));
var aux = listMatrix.Select(keyValuePair => (CustomKeyValuePair<string, Matrix>) keyValuePair).ToArray();
xmlSerializer.Serialize(stream, aux); // I serialize an array to make easy the deserailizer
stream.Close();
// Deserialization
stream = new FileStream(path, FileMode.Open);
var result = (CustomKeyValuePair<string, Matrix>[])xmlSerializer.Deserialize(stream);
stream.Close();
}
I am creating an app that fetches fuel prices from a web service. I want to deserialise the prices a IDictionary of Price objects with the fuel as the key (similar to this).
I had created a setter to do this but have since found out that the serialisation uses the Add method rather than the setter for lists. Is there a way of doing this using the serialisation API or will I have to write custom serialisation code?
The XML looks like this
<?xml version="1.0"?>
<prices>
<price fuel="Petrol">152.43</price>
<price fuel="Diesel">147.53</price>
</prices>
The code looks like this
[XmlRoot("prices")]
public class FuelPrices
{
private IDictionary<Fuel, Price> prices = new Dictionary<Fuel, Price>();
// This is used for serialising to XML
[XmlElement("price")]
public ICollection<Price> Prices
{
get
{
return prices.Values;
}
set
{
prices = new Dictionary<Fuel, Price>();
foreach (Price price in value)
{
prices[price.Fuel] = price;
}
}
}
// These properties are used to access the prices in the code
[XmlIgnore]
public Price PetrolPrice
{
get
{
Price petrolPrice;
prices.TryGetValue(Fuel.Petrol, out petrolPrice);
return petrolPrice;
}
}
[XmlIgnore]
public Price DieselPrice
{
get
{
Price dieselPrice;
prices.TryGetValue(Fuel.Diesel, out dieselPrice);
return dieselPrice;
}
}
}
You can write a wrapper around a dictionary along the lines of
sealed class DictionaryWrapper<K, T> : ICollection<T>
{
private readonly Func<T, K> m_keyProjection ;
private readonly IDictionary<K, T> m_dictionary ;
// expose the wrapped dictionary
public IDictionary<K, T> Dictionary { get { return m_dictionary ; }}
public void Add (T value)
{
m_dictionary[m_keyProjection (value)] = value ;
}
public IEnumerator<T> GetEnumerator ()
{
return m_dictionary.Values.GetEnumerator () ;
}
// the rest is left as excercise for the reader
}
and use it like this
private DictionaryWrapper<Fuel, Price> pricesWrapper =
new DictionaryWrapper<Fuel, Price> (
new Dictionary<Fuel, Price> (), price => price.Fuel) ;
[XmlElement("price")]
public ICollection<Price> Prices
{
get { return pricesWrapper ; } // NB: no setter is necessary
}
if you do not want to write custom serialization - you can do this:
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication2
{
public class Program
{
[XmlType("price")]
public class Price
{
[XmlText]
public double price { get; set; }
[XmlAttribute("fuel")]
public string fuel { get; set; }
}
[XmlType("prices")]
public class PriceList : List<Price>
{
}
static void Main(string[] args)
{
//Serialize
var plist = new PriceList()
{
new Price {price = 153.9, fuel = "Diesel"},
new Price {price = 120.6, fuel = "Petrol"}
};
var serializer = new XmlSerializer(typeof(PriceList));
var sw = new StringWriter();
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(sw, plist, ns);
var result = sw.ToString();//result xml as we like
//Deserialize
var sr = new StringReader(result);
var templist = (PriceList)serializer.Deserialize(sr);
var myDictionary = templist.ToDictionary(item => item.fuel, item => item.price);
}
}
}
and if you need custom serialization - look at this post:Why isn't there an XML-serializable dictionary in .NET?