Avoid expanding linked objects when serialising - c#

I am using JSON.NET to serialize some c# objects into JSON (and then write to a file).
My two main classes are:
public class Reservoir {
private Well[] mWells;
public Well[] wells {
get { return mWells; }
set { mWells = value; }
}
}
and
public Well() {
private string mWellName;
private double mY;
private double mX;
public string wellName {
get { return mWellName; }
set { mWellName = value; }
}
public double y {
get { return mY; }
set { mY = value; }
}
public double x {
get { return mX; }
set { mX = value; }
}
private Well[] mWellCorrelations;
}
The problem is that the output looks like:
'{"wells":[{"wellName":"B-B10","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B12","y":152.71005958395972,"x":459.02158140110026,"wellCorrelations":[{"wellName":"B-B13","y":475.0,"x":495.14804408905263,"wellCorrelations":[{"wellName":"B-B11","y":25.0,"x":50.0,"wellCorrelations":[]}
i.e. the associated wells of each well object are expanded as objects themselves and this becomes a serious problem of space and time when there lots of associated objects.
I suppose I would have preferred something like:
'{"wells":[{"wellName":"B-B10","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B12"}], {"wellName":"B-B11","y":217.04646503367468,"x":469.5776343820333,"wellCorrelations":[{"wellName":"B-B13"}
i.e maintaining only the well name as the link (assume its unique).
Is there a way to do this with JSON.NET?
You have set
serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
but it doesn't make any difference.

You could add a new readonly property called WellCorrelations that only got the names of the well correlations, and slap a JsonIngore attribute on your mWellCorrelations, like so:
[JsonIgnore]
private Well[] mWellCorrelations;
public string[] WellCorrelations
{
get { return mWellCorrelations.Select(w => w.wellName).ToArray(); }
}
http://james.newtonking.com/projects/json/help/html/ReducingSerializedJSONSize.htm
That way, the serializer will only serialize the names of the correlated wells.

Related

Pass properties from array to a new class

i am working with a .net application where i have a web service that returns values in array form and now this array values i want to pass to a class and also as a reference to a private object. But since i am fresh new in programming i do not know how where an with what logic to start.
This is the private obj i created and i want to pass those references where CT is the array type and clsIn is the info that comes from another class but i have no idea how to pass neither of them.
private object TotInfo(clsIn In, CT ct)
{
TotInfo objFromCD = new TotInfo();
return objFromCD;
}
And here is the new class i have created that where i want to pass all the values from clsIn and CT:
public class TotInfo
{
// Object properties
private string LAST_OFFER;
private string LAST_OFFER_DATE;
private string CLOSING_REASON;
private string _NO;
private string _STATUS;
#region "GET/SET Property"
public string NO
{
get { return _NO; }
set { _NO = value; }
}
public string LAST_OFFER
{
get { return _LAST_OFFER; }
set { _LAST_OFFER = value; }
}
public string LAST_OFFER_DATE
{
get { return _LAST_OFFER_DATE; }
set { _LAST_OFFER_DATE = value; }
}
public string CLOSING_REASON
{
get { return _CLOSING_REASON; }
set { _CLOSING_REASON = value; }
}
public string STATUS
{
get { return _STATUS; }
set { _STATUS = value; }
}
#endregion
#region "Costruttori"
public CardsTotInfo() { }
public CardsTotInfo(string No, string lastOffer, string lastOfferDate, string closingReason, string status)
{
this.NO = No;
this.LAST_OFFER = lastOffer.ToUpper();
this.LAST_OFFER_DATE = lastOfferDate.ToUpper();
this.CLOSING_REASON = closingReason.ToUpper();
this.STATUS = status.ToUpper();
}
}
I have passed, or better say i think i have passed in the correct way the values of clsIn but i do not know how to pass the properties of the array type CT[].
I really need help.
Thank you in advance.
If CT is an object array and the data you get from the web service always comes in the same order, for instance using an arbitrary example:
object[] CT = { 1, DateTime.Now, "foo", true }
If you know that each property data inside the array will always be at the same index (you will always have a int in index 0 representing an Id, and a DateTime on index 1 representing the last offer day and so on)
I would say you need to set each property "manually":
private object TotInfo(clsIn In, CT ct)
{
TotInfo objFromCD = new TotInfo();
//get data from DB
//set the data from the array into the class properties
objFromCD.Id = (int)ct[0];
objFromCD.LastOfferDate = (DateTime)ct[1];
objFromCD.ClosingReason = (string)ct[2];
objFromCD.Available = (bool)ct[3];
return objFromCD;
}

Xml List Serialization and Node Type Names

Ive come across multiple questions and answers on here but none specific to my situation.
I have a class 'Entity' with multiple classes that extend off of it. I want the serialization to hit the list and understand and use the type of each item for the node name.
Now, I can use what is commented out (define each array item in the main class and define the name of such by using [XmlArrayItem("Subclass1", typeof(subclass1)] but I want to keep all definitions in their subclass and I will be having too many subclasses to define everything in the main entity class...Is there anyway to achieve this?
I have tried using [XmlType(TypeName="...")] for the subclasses and so on but that did not work.
[Serializable]
[XmlInclude(typeof(Subclass1))]
[XmlRoot("Entity")]
public class Entity{
[XmlArray("CausedBy")]
//[XmlArrayItem("Subclass1", typeof(subclass1))]
//[XmlArrayItem("Sublcass2", typeof(Subclass2))]
public List<Entity> CausedBy { get; set; }
}
[Serializable]
[XmlRoot("Subclass1")]
[XmlInclude(typeof(Subclass2))]
public class Subclass1:Entity{
//Code...
}
[Serializable]
[XmlRoot("Subclass2")]
public class Subclass2:Subclass1{
//Code...
}
Serializing the above code after creating an entity and adding a Subclass1 and Subclass2 to the list 'CausedBy' class results in the following:
<Entity>
<CausedBy>
<Entity ... xsi:type="SubClass1" />
<Entity ... xsi:type="SubClass2" />
</CausedBy>
<Entity>
I would like the output to show:
<Entity>
<CausedBy>
<SubClass1 .../>
<SubClass2 .../>
</CausedBy>
<Entity>
Since I totally failed to read the question to begin with, here's a new answer (it's a bit of a tl;dr, so you can always skip to the end and follow the link):
It isn't possible to get the built in serializer class to work because you don't wish to add the attributes that it needs to be able to operate. Your only option is to seralize the class yourself, however, this need not be as tedious as it sounds; I had a similar issue a few years ago with DataGridView in virtual mode and produced a generic virtualizer that could be used to virtualize the data for display; it used a custom attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class showColumnAttribute : System.Attribute
{
///<summary>Optional display format for column</summary>
public string Format;
///<summary>Optional Header string for column<para>Defaults to propety name</para></summary>
public string Title;
///<summary>Optional column edit flag - defaults to false</summary>
public bool ReadOnly;
///<summary>Optional column width</summary>
public int Width;
///<summary>
///Marks public properties that are to be displayed in columns
///</summary>
public showColumnAttribute()
{
Format = String.Empty;
Title = String.Empty;
ReadOnly = false;
Width = 0;
}
}
And a constructor:
///<summary>
///Extracts the properties of the supplied type that are to be displayed
///<para>The type must be a class or an InvalidOperationException will be thrown</para>
///</summary>
public Virtualiser(Type t)
{
if (!t.IsClass)
throw new InvalidOperationException("Supplied type is not a class");
List<VirtualColumnInfo> definedColumns = new List<VirtualColumnInfo>();
PropertyInfo[] ps = t.GetProperties();
MethodInfo mg, ms;
for (int i = 0; i < ps.Length; i++)
{
Object[] attr = ps[i].GetCustomAttributes(true);
if (attr.Length > 0)
{
foreach (var a in attr)
{
showColumnAttribute ca = a as showColumnAttribute;
if (ca != null)
{
mg = ps[i].GetGetMethod();
if (mg != null)
{
ms = ps[i].GetSetMethod();
definedColumns.Add
(
new VirtualColumnInfo
(
ps[i].Name, ca.Width, ca.ReadOnly, ca.Title == String.Empty ? ps[i].Name : ca.Title,
ca.Format, mg, ms
)
);
}
break;
}
}
}
}
if (definedColumns.Count > 0)
columns = definedColumns.ToArray();
}
This extracts the public properties of the class and supplies marked items to the DataGridView as columns together with a header, format, etc.
The effect of all of this (and the rest of the missing code) was that any type could be virtualized in a dataGridView simply by tagging public properties and calling the virtualizer once for a given type:
#region Virtualisation
static readonly Virtualiser Virtual = new Virtualiser(typeof(UserRecord));
[XmlIgnore] // just in case!
public static int ColumnCount { get { return Virtual.ColumnCount; } }
public static VirtualColumnInfo ColumnInfo(int column)
{
return Virtual.ColumnInfo(column);
}
public Object GetItem(int column)
{
return Virtual.GetItem(column, this);
}
/*
** The supplied item should be a string - it is up to this method to supply a valid value to the property
** setter (this is the simplest place to determine what this is and how it can be derived from a string).
*/
public void SetItem(int column, Object item)
{
String v = item as String;
int t = 0;
if (v == null)
return;
switch (Virtual.GetColumnPropertyName(column))
{
case "DisplayNumber":
if (!int.TryParse(v, out t))
t = 0;
item = t;
break;
}
try
{
Virtual.SetItem(column, this, item);
}
catch { }
}
#endregion
The number of columns, their properties and order can be specified automatically by creating a number of public properties derived from the class data:
#region Display columns
[showColumn(ReadOnly = true, Width = 100, Title = "Identification")]
public String DisplayIdent
{
get
{
return ident;
}
set
{
ident = value;
}
}
[showColumn(Width = 70, Title = "Number on Roll")]
public int DisplayNumber
{
get
{
return number;
}
set
{
number = value;
}
}
[showColumn(Width = -100, Title = "Name")]
public string DisplayName
{
get
{
return name == String.Empty ? "??" : name;
}
set
{
name = value;
}
}
#endregion
This would virtualize any class for dataGridView to display and edit data and I used it many times over the years and the extraction of properties to display is exactly what is required for XML serialization, indeed, it has a lot of the same characteristics.
I was going to adapt this method to do the same job for XML serialization but someone has already done it at https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=474453, I hope you can make use of this method to solve your problem.
This works for me:
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Entity entity = new Entity();
entity.CausedBy = new List<Entity>();
entity.CausedBy.Add(new Subclass1());
entity.CausedBy.Add(new Subclass2());
entity.CausedBy.Add(new Subclass2());
entity.CausedBy.Add(new Subclass1());
entity.CausedBy.Add(new Subclass1());
entity.Save(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "Test.txt"));
}
}
[Serializable]
[XmlRoot("Entity")]
public class Entity
{
[XmlArray("CausedBy")]
[XmlArrayItem("SubClass1", typeof(Subclass1))]
[XmlArrayItem("SubClass2", typeof(Subclass2))]
public List<Entity> CausedBy { get; set; }
}
[Serializable]
[XmlRoot("Subclass1")]
public class Subclass1 : Entity
{
[XmlIgnore]
String t = DateTime.Now.ToShortDateString();
public String SubClass1Item { get { return "Test1 " + t; } set { } }
}
[Serializable]
[XmlRoot("Subclass2")]
public class Subclass2 : Entity
{
[XmlIgnore]
String t = DateTime.Now.ToString();
public String SubClass2Item { get { return "Test2 " + t; } set { } }
}
It produces:
<Entity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CausedBy>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
<SubClass2>
<SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item>
</SubClass2>
<SubClass2>
<SubClass2Item>Test2 20/09/2017 01:06:55</SubClass2Item>
</SubClass2>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
<SubClass1>
<SubClass1Item>Test1 20/09/2017</SubClass1Item>
</SubClass1>
</CausedBy>
</Entity>

Deserializing xml to list object returning null

I'm trying to deserialize xml data using xmlreader into a list object but I am getting a null back from my call. Here is a sample of my xml data...
<ExceptionLog>
<ExceptionLogData MessageCount="1" SourceDateTime="2016-02-08T09:32:41.713" MinSourceDateTime="2016-02-08T09:32:41.713" DataId="610029" MaxExceptionLogID="610029" MessageText="INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session. Session not found, missing session hash: hX7K7LONeTilw5RfGT432g==
This is expected, it can happen if the session has expired and swept away, or if the user logs out, or if its just someone trying to hack in. " MachineName="VERTEXDPORTSQL1" AppDomainName="VTMS.Windows.SalesforceServicingAgent.exe" ProcessName="VTMS.Windows.SalesforceServicingAgent" />
<ExceptionLogData MessageCount="1" SourceDateTime="2016-02-08T09:22:39.340" MinSourceDateTime="2016-02-08T09:22:39.340" DataId="610028" MaxExceptionLogID="610028" MessageText="INVALID_SESSION_ID: Invalid Session ID found in SessionHeader: Illegal Session. Session not found, missing session hash: rtZTrLk2f99iVttLoz31tg==
This is expected, it can happen if the session has expired and swept away, or if the user logs out, or if its just someone trying to hack in. " MachineName="VERTEXDPORTSQL1" AppDomainName="VTMS.Windows.SalesforceServicingAgent.exe" ProcessName="VTMS.Windows.SalesforceServicingAgent" />
</ExceptionLog>
This is the object class code that I am trying to create...
public class ExceptionLog {
public ExceptionLog() {
ExceptionLogData = new List<ExceptionLogExceptionLogData>();
}
public List<ExceptionLogExceptionLogData> ExceptionLogData { get; set; }
}
public class ExceptionLogExceptionLogData {
private DateTime _sourceDateTimeField;
private DateTime _minSourceDateTimeField;
private uint dataIdField;
private uint _maxExceptionLogIdField;
private string _messageTextField;
private string _machineNameField;
private string _appDomainNameField;
private string _processNameField;
public byte MessageCount { get; set; }
public DateTime SourceDateTime {
get {
return _sourceDateTimeField;
}
set {
_sourceDateTimeField = value;
}
}
public DateTime MinSourceDateTime {
get {
return _minSourceDateTimeField;
}
set {
_minSourceDateTimeField = value;
}
}
public uint DataId {
get {
return dataIdField;
}
set {
dataIdField = value;
}
}
public uint MaxExceptionLogID {
get {
return _maxExceptionLogIdField;
}
set {
_maxExceptionLogIdField = value;
}
}
public string MessageText {
get {
return _messageTextField;
}
set {
_messageTextField = value;
}
}
public string MachineName {
get {
return _machineNameField;
}
set {
_machineNameField = value;
}
}
public string AppDomainName {
get {
return _appDomainNameField;
}
set {
_appDomainNameField = value;
}
}
public string ProcessName {
get {
return _processNameField;
}
set {
_processNameField = value;
}
}
}
And finally here is how I am trying to deserialize the data...
using (var dataReader = sqlCommand.ExecuteXmlReader())
{
var serializer = new XmlSerializer(typeof(ExceptionLog));
var returnDataList = serializer.Deserialize(dataReader) as List<ExceptionLogExceptionLogData>;
return returnDataList;
}
What have I missed or what am I doing wrong?
I have another approach that I can use until I figure this out and that is the old fashioned way of creating my object list and programmatically populating it with my objects on the fly - not very graceful but for the time being it works.
TIA
XmlSerializer is defined of type ExceptionLog but you're then casting the result to List
var serializer = new XmlSerializer(typeof(ExceptionLog));
var returnDataList = serializer.Deserialize(dataReader) as List<ExceptionLogExceptionLogData>;
The casting should be to the type of serializer:
var returnDataList = serializer.Deserialize(dataReader) as ExceptionLog;
I didn't check all the elements but you should also mark ExceptionLogData with XmlElement attribute.
[XmlElement]
public List<ExceptionLogExceptionLogData> ExceptionLogData { get; set; }
There might be some issues with the other properties but this should address the problem in the question

How to make a arrayclass access with this[][]

I want to create an Matrix class for some calculations. At my research i was stumble over some performance discussions Array[,] vs Array[][] and they conclusion was always use Array[][] if you want it fast
When i'm trying to provide access to an value via [][] it seems like i miss something important because i cant create an property like this
public double this[int iRow][int iCol]
{
get { return matrix[iRow][iCol]; }
set { matrix[iRow][iCol] = value; }
}
my problem is the second [] because the following works well
public double this[int iRow,int iCol]
{
get { return matrix[iRow][iCol]; }
set { matrix[iRow][iCol] = value; }
}
So what do i need to add in this class to let it work with [][] ?
public class Matrix
{
private bool _isSquare;
private double[][] matrix;
public double this[int iRow,int iCol]
{
get { return matrix[iRow][iCol]; }
set { matrix[iRow][iCol] = value; }
}
}
You can't do this in one class.
var b = a[1][2];
is the same as
var temp = a[1];
var b = temp[2];
They are two separate indexing operations.
Your first indexer would need to return an instance of some class that also has an indexer.
Using Indexers (C#)
Not sure what you are trying to do but if your matrix was the following you could still do what you are looking for
public class M
{
public double[][] Matrix { get; private set; }
public M()
{
Matrix = new double[2][]{new double[2], new double[2]};
}
}
M n = new M();
n.Matrix[0][0] = 1.0;

How to design collection in C#

I have a class MySet
class MySet
{
.......
}
This class will declare a reference to another type
(i.e)
class MySubSet
{
....
}
The purpose of the type MySubset is to supply "subset id" and a collection of integers to
the type MySet.
Which one of the followings is the correct implementation
(1)
class MySet
{
int mySetID;
MySubSet subset = new MySubSet();
public int MySetID
{
get { return mySetID; }
set { mySetID = value; }
}
public MySubSet MySubSet
{
get { return subset; }
set { subset = value; }
}
}
class MySubSet
{
int subsetID;
List<int> subset = new List<int>();
public List<int> SubSet
{
get { return subset; }
set { subset = value; }
}
public int SubSetID
{
get { return subsetID; }
set { subsetID = value; }
}
}
(2)
class MySet
{
int mySetID;
AnotherSubSet subset = new AnotherSubSet();
public int MySetID
{
get { return mySetID; }
set { mySetID = value; }
}
public AnotherSubSet MySubSet
{
get { return subset; }
set { subset = value; }
}
}
class AnotherSubSet : List<int>
{
int subsetID;
List<int> lst = new List<int>();
public int SubSetID
{
get { return subsetID; }
set { subsetID = value; }
}
}
If both are worst design consideration help me to implement the one that I could follow.
MySet doesn't look like a collection to me. It's just a class.
I'd rename it to ´MyEntity´or something like that.
List<MyEntity> mySet = new List<MyEntity>();
From all the information you've provided, I would do this:
public class MyEntity
{
public int ID { get; set; } // shortcut
public List<int> Numbers = new List<int> { get; set; } // shortcut
}
Sorry, I don't have /Net3.0 to hand so can't check the constructor of the list with the shortcut get/set but its the theory that counts...
The first version is better (as improved upon by ck) - use composition instead of inheritance. You are advised not to add properties to collections, which is effectively what you're doing in version 2. Collections should contain their items only. Someone else may be able to expand on the reasons for this, as I am not an expert, but it does cause serialization problems.
Number 2 is better, use inheritence not composition for this pattern, - because fundementally, it is a collection. It does not contain a collection. Inheritance gives you all the functionality of the base class without the need to write pass-through functions. If you want to add a new item to the collection, using composition, you either have to add a pass through method for the Add() method to class MySubSet:
class MySubSet
{
int subsetID;
List<int> subset = new List<int>();
public List<int> SubSet
{
get { return subset; }
set { subset = value; }
}
public void Add(int i) { subset.Add(i); } // pass through to subset.Add()
}
or you have to use the following non-intuitive and confusing syntax...
MySet.MySubSet.SubSet.Add(67);
with inheritence, all you need is
MySet.MySubSet.Add(67);

Categories

Resources