Exclude (ignore) object during serialization - c#

Have a ordinary hierarchy of objects that inherit common to all interface or abstract class.
public abstract class AActivation
{
public bool IsActivated
{ get; set; }
}
public class Root : AActivation
{
public int Value
{ get; set; }
public List<LevelOne> Childs
{ get; private set; }
public Root()
{
Value = 1000;
Childs = new List<LevelOne>();
}
}
public class LevelOne : AActivation
{
static int _i = 0;
public int Value
{ get; set; }
public List<LevelTwo> Childs
{ get; private set; }
public LevelOne()
{
Value = (++_i);
Childs = new List<LevelTwo>();
}
}
public class LevelTwo : AActivation
{
static int _i = 100;
public int Value
{ get; set; }
public LevelTwo()
{
Value = (++_i);
}
}
Objective is serialize the given object tree in XML-file excluded from serialization objects whose property IsActivated equal false.
public class Serializer
{
public static void Serialize<T>(T serializableObject , string path)
where T: AActivation , new()
{
using(StreamWriter sw = new StreamWriter(path))
{
XmlSerializer writer = new XmlSerializer(typeof(T));
writer.Serialize(sw , serializableObject);
sw.Flush();
sw.Close();
}
}
}
class Program
{
static void Main()
{
var random = new Random();
Root r = new Root();
for(int i = 0 ; i < 10 ; i++)
{
var levelOne = new LevelOne() { IsActivated = random.Next(2) > 0};
r.Childs.Add(levelOne);
for(int j = 0 ; j < 5 ; j++)
{
var levelTwo = new LevelTwo() { IsActivated = random.Next(2) > 0};
levelOne.Childs.Add(levelTwo);
}
}
string path = Path.Combine(Environment.CurrentDirectory , Path.GetRandomFileName() + ".xml");
Serializer.Serialize(r , path);
Console.ReadKey();
}
}
Now I see several solutions:
Inherit for all classes IXmlSerializable interface. But I don't want to interfere in the process of serialization as a serious way. And I need to change only the writing behavior, but also have to restore the reading behavior. After all, have to restore the default behavior, only one line of code.
if(obj.IsActivated == false) return;
Create your own XmlWriter. This lower level of XML model and again I don't want to interfere in its construction.
I hope there is a simple solution to my problem, that will not be so much to intervene in the process of serialization.

Related

StackOverflowException when deserializing json for self referencing class instances

I have a class that contains Range[] as property and Range class is a self referencing class. I used [JsonIgnore] to prevent StackoverflowException but it works for only Serialize not Deserialize. How can I fix this?
using System;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
namespace testoverflow
{
class Program
{
public static void Main(string[] args)
{
GlobalVariable.Json = "[{\"TotalBytesReceived\":0,\"Id\":\"b03750fb291a46708f8e1a7409553075\",\"NofThread\":8,\"Speed\":0,\"Progress\":0.0,\"FilePath\":\"C:\\\\Users\\\\kafeinaltor\\\\Downloads\",\"RangeDir\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\",\"Url\":\"http://ipv4.download.thinkbroadband.com/20MB.zip\",\"Ranges\":[{\"Start\":0,\"End\":9223372036854775806,\"TotalBytesReceived\":0,\"IsDownloaded\":false,\"FileId\":\"87cd7715dc0740c1b82ddd681bf2523d\",\"Size\":9223372036854775807,\"Status\":4,\"IsIdle\":false,\"SaveDir\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\",\"FilePath\":\"C:\\\\Users\\\\kafeinaltor\\\\AppData\\\\Roaming\\\\87cd7715dc0740c1b82ddd681bf2523d\",\"Md5Checksum\":null}],\"Info\":null,\"DownloadRequestMessage\":null}]";
var a = new MTDO();
Console.WriteLine(GlobalVariable.Json);
Console.ReadKey(true);
}
public static class GlobalVariable
{
public static string Json { get; set; }
}
public class MTDO
{
public MTDO()
{
Ranges = new Range[]
{
new Range(0L, 100L, ""),
new Range(101L, 200L, "")
};
Id = Guid.NewGuid().ToString("N");
Reminder.AddOrUpdate(this);
}
public string Id { get; set; }
public Range[] Ranges{ get; set; }
}
public class Range
{
public long Start { get; set; }
public long End { get; set; }
public string SaveDir { get; set; }
public long TotalBytesReceived{ get; set; }
public Range(long start, long end, string saveDir)
{
this.Start = start;
this.End = end;
this.SaveDir = Guid.NewGuid().ToString();
}
[JsonIgnore]
public Range Remaining
{
get
{
return new Range(Start + TotalBytesReceived, End, SaveDir);
}
}
}
public class Reminder
{
public Reminder()
{
}
public static void AddOrUpdate(MTDO mtdo)
{
var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
if (list == null)
list = new List<MTDO>();
var exists = list.Any(x => x.Id == mtdo.Id);
if (!exists)
list.Add(mtdo);
else
{
var i = list.Select((x, j) => new {val = x, index = j})
.First(x => x.val.Id == mtdo.Id).index;
list[i] = mtdo;
}
WriteJson(list);
}
public static List<MTDO> ReadList()
{
var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
if (list == null)
list = new List<MTDO>();
return list;
}
static string Read()
{
try
{
return GlobalVariable.Json;
}
catch
{
return "";
}
}
static void WriteJson(List<MTDO> list)
{
GlobalVariable.Json = JsonConvert.SerializeObject(list);
}
}
}
}
UPDATE: I have updated myquestion adding minimum reproducable code in Console Application. You can copy/paste and run directly.
The problem is that you have an infinite recursion:
You call MTDO constructor
Inside MTDO constructor you call Reminder.AddOrUpdate(this);
Inside that method you have var list = JsonConvert.DeserializeObject<List<MTDO>>(Read());
Which calls MTDO constructor again (step 1)
These steps keep repeating until you get StackOverflowException.

How to Serialize Object to Xml

The class I want to store:
[Serializable]
public class Storagee
{
int tabCount;
List<string> tabNames;
List<EachItemListHolder> eachItemsHolder;
public void PreSetting(int count, List<string> strings, List<EachItemListHolder> items)
{
tabCount = count;
tabNames = strings;
eachItemsHolder = items;
}
public void PreSetting(int count ) //debug purpose
{
tabCount = count;
}
public int GetTabCount() { return tabCount; }
public List<string> GetTabNames() { return tabNames; }
public List<EachItemListHolder> GetListEachItemListHolder() { return eachItemsHolder; }
}
Serializing class:
namespace Book
{
class SaveAndLoad
{
public void SaveAll(Storagee str)
{
var path = #"C:\Temp\myserializationtest.xml";
using (FileStream fs = new FileStream(path, FileMode.Create))
{
XmlSerializer xSer = new XmlSerializer(typeof(Storagee));
xSer.Serialize(fs, str);
}
}
public Storagee LoadAll()
{
var path = #"C:\Temp\myserializationtest.xml";
using (FileStream fs = new FileStream(path, FileMode.Open)) //double
{
XmlSerializer _xSer = new XmlSerializer(typeof(Storagee));
var myObject = _xSer.Deserialize(fs);
return (Storagee)myObject;
}
}
}
}
Main method (Window form):
class Book
{
List<EachTab> eachTabs;
Storagee storagee;
SaveAndLoad saveAndLoad;
eachTabs = new List<EachTab>();
storagee = new Storagee();
saveAndLoad = new SaveAndLoad();
void Saving()
{
int count = UserTab.TabCount; // tab counts
storagee.PreSetting(count);
saveAndLoad.SaveAll(storagee);
}
}
It makes xml file but doesn't save data.
I tried the serializing code in different project and it worked.
but it doesn't in this solution
since I'm kind of new to coding I don't know what the problem is
especially serializing part.
serializing codes are copied and pasted with little tweak
It makes xml file but doesn't save data.
It doesn't save any data because your class does not provide any data that it can serialize. XmlSerializer only serializes public fields and properties and the Storagee class doesn't have any.
You could, for example, change your public getter methods to public properties:
public int TabCount { get; set; }
public List<string> TabNames { get; set; }
public List<string> EachItemsHolder { get; set; }
Alternatively, if using public properties is not an option, you could also look into using custom serialization by implementing IXmlSerializable.

Automapper ProjectTo not mapping properties to derived types, although the mapping configuration is set up to only know derived types

I am currently facing an issue involving automapper.
I am trying to map totally unrelated types, which works perfectly, except for properties of the projected class. I am currently unable to map them to derived types. (the information/data is all available in the source type, so it's not about EF and inheritence... that part was solved already).
Source/destination types
#region DestinationTypes
private class A
{
public List<B> Bs { get; set; }
}
private class B
{
public string Id { get; set; }
}
private class C : B
{
public string TestC { get; set; }
}
#endregion DestinationTypes
#region SourceTypes
private class D
{
public List<E> Bs { get; set; }
}
private class E
{
public string Id { get; set; }
public string TestC { get; set; }
}
#endregion SourceTypes
Mapping configuration
var _cfg =new MapperConfigurationExpression();
_cfg.CreateMap<E, C>();
_cfg.CreateMap<D, A>();
var _config = new MapperConfiguration(_cfg);
Version: 6.2.2
Expected behavior
I expected an iqueryable where the List Bs is filled with instances of type C.
Actual behavior
The List Bs is filled with instances of type B, instead of with type C.
Steps to reproduce:
[TestMethod]
public void TestProjection()
{
int _total = 10;
var _listOfD = new List<D>();
for (int _i = 0; _i < 10; _i++)
{
var _d = new D();
_d.Bs = new List<E>();
for (int _j = 0; _j < _total; _j++)
{
var _e = new E();
_e.Id = $"{_i}_{_j}";
_e.TestC = $"C{_i}_{_j}";
_d.Bs.Add(_e);
}
_listOfD.Add(_d);
}
var _cfg =new MapperConfigurationExpression();
_cfg.CreateMap<E, C>();
_cfg.CreateMap<D, A>();
var _config = new MapperConfiguration(_cfg);
//_config.ResolveTypeMap(typeof(E), typeof(C));
//_config.CompileMappings();
var _x = _listOfD.AsQueryable().ProjectTo<A>(_config);
foreach (var _z in _x)
{
var _item = (C)_z.Bs.FirstOrDefault();
Debug.Assert(_item.TestC != null && _item.Id != null);
}
}

How to specify a different default values for XmlElement other than the one in its constructor?

I have these 2 classes
[XmlType]
public class Child
{
public Child()
{
X = false;
Y = -100;
}
[XmlAttribute]
public bool X { get; set; }
[XmlAttribute]
public int Y { get; set; }
}
[XmlRoot]
public class Parent
{
public Parent()
{
C = new Child()
{
X = true;
}
;
}
[XmlElement]
public child C { get; set; }
}
when I try to parse a parent with child object that does not specify a value of x, I need x to be true not false. Example:
string xmlText = "<Parent><C y='1000'/></Parent>";
Parent p;
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlText)))
{
using (var reader = XmlDictionaryReader.CreateTextReader(memoryStream, Encoding.UTF8, XmlDictionaryReaderQuotas.Max, null))
{
var xmlDeserializer = new XmlSerializer(typeof(parent));
p = xmlDeserializer.Deserialize(reader) as Parent;
}
}
if(p.C.X)
{
Console.WriteLine("p.C.X is true");.
}
else
{
Console.WriteLine("p.C.X is false");
}
Then if we print p.C.X it will be false not true. How to solve this problem?
Note:
I have updated the question, X was int in the beginning and then I changed it be bool (that's why the answers will refer to X as an int not a bool).
If there is no "invalid value", one solution I see is to use a boolean flag in the child class to indicate whether the X property was set or not.
This flag starts as true, but then is set to false in the X setter.
Then in the parent's C setter, you need to check that flag.
[XmlType]
public class Child
{
private int x;
public Child()
{
x = 10;
UsingDefaulXValue = true;
Y = -100;
}
[XmlAttribute]
public int X
{
get
{
return x;
}
set
{
x = value;
UsingDefaulXValue = false;
}
}
[XmlIgnore]
public bool UsingDefaulXValue { get; private set; }
[XmlAttribute]
public int Y { get; set; }
}
[XmlRoot]
public class Parent
{
private const int DefaultX = 5;
private Child c;
public Parent()
{
// Careful to use property and not field
C = new Child();
}
[XmlElement]
public Child C
{
get
{
return c;
}
set
{
c = value;
if (c.UsingDefaulXValue)
{
c.X = DefaultX;
}
}
}
}
Trying it out:
void Main()
{
// Checking the child's default value
Child c1 = Deserialize<Child>("<Child/>");
Console.WriteLine(c1.X); // it will print 10.
Child c2 = Deserialize<Child>("<Child X='30'/>");
Console.WriteLine(c2.X); // it will print 30.
// Checking the parent's default value
Parent p1 = Deserialize<Parent>("<Parent></Parent>");
Console.WriteLine(p1.C.X); // it will print 5.
Parent p2 = Deserialize<Parent>("<Parent><C Y='1000'/></Parent>");
Console.WriteLine(p2.C.X); // it will print 5.
Parent p3 = Deserialize<Parent>("<Parent><C X='20' Y='1000'/></Parent>");
Console.WriteLine(p3.C.X); // it will print 20.
}
T Deserialize<T>(string xmlText)
{
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(xmlText)))
{
using (var reader = XmlDictionaryReader.CreateTextReader(memoryStream, Encoding.UTF8, XmlDictionaryReaderQuotas.Max, null))
{
var xmlDeserializer = new XmlSerializer(typeof(T));
return (T)xmlDeserializer.Deserialize(reader);
}
}
}
Even if "x" is not set by the client xml, it will still get de-serialized as a zero so you wouldn't necessarily know if x was actually set to that intentionally or simply omitted.
Now if you're never expecting a zero as input for x, then you can simply modify the setter on the child "c" property in your parent class:
[XmlRoot]
public class parent
{
private child _c;
[XmlElement]
public child c { get
{
return this._c;
}
set
{
this._c = value;
if (this._c.x == 0) this._c.x = 5;
}
}
}
and you can of course change your "assumed number that would never be used" to some other number say -1:
[XmlType]
public class child
{
private int _x = -1;
[XmlAttribute]
public int x { get { return this._x; } set { this._x = value; } }
[XmlAttribute]
public int y { get; set; }
}
[XmlRoot]
public class parent
{
private child _c;
[XmlElement]
public child c { get
{
return this._c;
}
set
{
this._c = value;
if (this._c.x == -1) this._c.x = 5;
}
}
}
It's not real pretty either way I know...

XMLSerializer changes name of elements

I'm trying to serialize object for import to another Software and the issue is, that elements in XML that is to be imported contain ":" (p.e.: < ftr:filter>).
I declared classes overriding those names with [XmlAttribute("ftr:filter")] and [XMLElement(ftr:differentFilter")], but serializer products different nodes. I bet it has something to do with encoding, but I'm not able to change the result (thought I changed encoding).
Example of classes:
public class ListPrijemkaRequest
{
[XmlAttribute("version")]
public string Version { get; set; }
[XmlAttribute("prijemkaVersion")]
public string PrijemkaVersion { get; set; }
[XmlElement("lst:requestPrijemka")]
public List<RequestPrijemka> Requests { get; set; }
}
public class RequestPrijemka
{
[XmlElement("ftr:filter")]
public RequestDateFilter Filter { get; set; }
}
Desired ooutput:
< lst:listPrijemkaRequest version="2.0" prijemkaVersion="2.0">
< lst:requestPrijemka>
< ftr:filter>
< ftr:dateFrom>2013-01-10</ftr:dateFrom>
< ftr:dateTill>2013-03-30</ftr:dateTill>
< /ftr:filter>
< /lst:requestPrijemka>
< /lst:listPrijemkaRequest>
Obtained output:
< lst_x003A_listPrijemkaRequest version="2.0" prijemkaVersion="2.0">
< lst_x003A_requestPrijemka>
< ftr_x003A_filter>
< ftr_x003A_dateFrom>2013-01-10</ftr_x003A_dateFrom>
< ftr_x003A_dateTill>2013-03-30</ftr_x003A_dateTill>
< /ftr_x003A_filter>
< /lst_x003A_requestPrijemka>
< /lst_x003A_listPrijemkaRequest>
If those "tags" ftr / lst are namespaces, there is no need to "hard-code" them, you can setup the serializer to use those namespaces.
http://msdn.microsoft.com/en-us/library/ms163161%28v=vs.110%29.aspx
Example (taken from XML Serialization and namespace prefixes)
[XmlRoot("Node", Namespace="http://your.companies.namespace")]
public class ListPrijemkaRequest {
[XmlElement("requestPrijemka")]
public List<RequestPrijemka> Requests { get; set; }
}
static class Program
{
static void Main()
{
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("lst", "http://your.companies.namespace");
XmlSerializer xser = new XmlSerializer(typeof(ListPrijemkaRequest));
xser.Serialize(Console.Out, new ListPrijemkaRequest(), ns);
}
}
If not, I don't think it's possible with "default" serialization.
Other options:
Use custom serialization (IXmlSerializable), see: http://www.codeproject.com/Articles/43237/How-to-Implement-IXmlSerializable-Correctly
Do post editing of the serialized file and do a string replace with the desired node names
But like I said in my comment, it is not recommended to use : in node names in the first place!
[XmlRoot("listPrijemkaRequest", Namespace = "http://your.companies.namespace/lst")]
public class ListPrijemkaRequest {
[XmlAttribute("version")]
public string Version { get; set; }
[XmlAttribute("prijemkaVersion")]
public string PrijemkaVersion { get; set; }
[XmlElement("requestPrijemka")]
public List<RequestPrijemka> Requests { get; set; }
}
public class RequestDateFilter
{
[XmlElement(ElementName = "dateFrom")]
public DateTime DateFrom { get; set; }
[XmlElement(ElementName = "dateTill")]
public DateTime DateTill { get; set; }
}
public class RequestPrijemka {
[XmlElement("filter", Namespace = "http://your.companies.namespace/ftr")]
public RequestDateFilter Filter { get; set; }
}
static class Program {
static void Main() {
var ns = new XmlSerializerNamespaces();
ns.Add("lst", "http://your.companies.namespace/lst");
ns.Add("ftr", "http://your.companies.namespace/ftr");
var xser = new XmlSerializer(typeof(ListPrijemkaRequest));
var obj = new ListPrijemkaRequest
{
Version = "2.0",
PrijemkaVersion = "2.0",
Requests = new List<RequestPrijemka>
{
new RequestPrijemka
{
Filter = new RequestDateFilter {DateFrom = DateTime.Now, DateTill = DateTime.Now}
}
}
};
xser.Serialize(Console.Out, obj, ns);
Console.ReadLine();
}
}
Produce this xml:
<?xml version="1.0" encoding="cp866"?>
<lst:listPrijemkaRequest xmlns:ftr="http://your.companies.namespace/ftr" version="2.0" prijemkaVersion="2.0" xmlns:lst="http://your.companies.namespace/lst">
<lst:requestPrijemka>
<ftr:filter>
<ftr:dateFrom>2014-07-17T16:17:47.0601039+03:00</ftr:dateFrom>
<ftr:dateTill>2014-07-17T16:17:47.061104+03:00</ftr:dateTill>
</ftr:filter>
</lst:requestPrijemka>
</lst:listPrijemkaRequest>
Looks similar with what you need.

Categories

Resources