I have a JSON structure (including POCO classes) with child-objects arrays like this:
"Object": [
{
"Name": "TestA",
"ChildObjects": [
{
"Name": "TestB"
"ChildObjects": [
{
"Name": "TestC"
...
}
]
}
]
When deserializing, I would like to keep a reference to the parent
object I've just created.
But I must get this reference before populating the child-object.(On the moment that I populate the child-object I must have the parent-object structure/reference accessible).
I've tried using a custom JsonConverter, but
I could not find a way to store or retrieve this relationship.
Rather than defining this as a serialization problem (how to serialize and deserialize a back-reference to a parent), it might make sense to define this as a class design problem, namely
Given a hierarchy of parents and children, how to ensure that child back-references to parents are automatically set correctly when adding them to their parents?
Once the problem is defined in this way and solved, correctness should be assured both during deserialization and during programmatic data creation, since the parent back-reference would never need to be serialized or deserialized.
One way to accomplish this would be to define a custom subclass of Collection<T> that automatically sets and clears parent back references.
First, define the following interface and collection:
public interface IHasParent<TParent> where TParent : class
{
TParent Parent { get; }
void OnParentChanging(TParent newParent);
}
public class ChildCollection<TParent, TChild> : Collection<TChild>
where TChild : IHasParent<TParent>
where TParent : class
{
readonly TParent parent;
public ChildCollection(TParent parent)
{
this.parent = parent;
}
protected override void ClearItems()
{
foreach (var item in this)
{
if (item != null)
item.OnParentChanging(null);
}
base.ClearItems();
}
protected override void InsertItem(int index, TChild item)
{
if (item != null)
item.OnParentChanging(parent);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
var item = this[index];
if (item != null)
item.OnParentChanging(null);
base.RemoveItem(index);
}
protected override void SetItem(int index, TChild item)
{
var oldItem = this[index];
if (oldItem != null)
oldItem.OnParentChanging(null);
if (item != null)
item.OnParentChanging(parent);
base.SetItem(index, item);
}
}
Then define your MyObject and RootObject types as follows:
public class MyObject : IHasParent<MyObject>
{
readonly ChildCollection<MyObject, MyObject> childObjects;
public MyObject() { this.childObjects = new ChildCollection<MyObject, MyObject>(this); }
public string Name { get; set; }
public IList<MyObject> ChildObjects { get { return childObjects; } }
#region IHasParent<MyObject> Members
[JsonIgnore]
public MyObject Parent { get; private set; }
public void OnParentChanging(MyObject newParent)
{
Parent = newParent;
}
#endregion
// Added to suppress serialization of empty ChildObjects collections to JSON.
public bool ShouldSerializeChildObjects() { return childObjects.Count > 0; }
}
public class RootObject
{
public RootObject() { this.Object = new List<MyObject>(); }
public List<MyObject> Object { get; set; }
}
Notes:
The collection IList<MyObject> ChildObjects in MyObject is get-only. Json.NET (and XmlSerializer for that matter) can successfully deserialize a get-only, pre-allocated collection.
The method ShouldSerializeChildObjects() is optional and prevents serialization of empty ChildObjects [] array values.
Since ObservableCollection<T> is itself a subclass of Collection<T>, you could chose it as the base class for ChildCollection<TParent, TChild> if you require notifications when items are added or removed.
The Parent property is marked with [JsonIgnore] to prevent its serialization.
Sample fiddle including some basic unit tests.
For clearer understanding dbc's answer let me simplify it.
Let's take example of setting parent for RootObject's item called MyObject:
{
"Object":[
{
"Name": "TestA"
}
]
}
Define collection:
public class Items : Collection<MyObject>
{
private RootObject Owner;
public Items(RootObject owner)
{
Owner = owner;
}
protected override void InsertItem(int index, MyObject item)
{
item.Parent = Owner;
base.InsertItem(index, item);
}
}
Define MyObject and RootObject:
public class MyObject
{
[JsonIgnore]
public RootObject Parent { get; set; }
public string Name { get; set; }
}
public class RootObject
{
public RootObject() { ChildObjects = new Items(this); }
public Items ChildObjects { get; }
}
You needn't JsonConverter.
You can create POCO classes that represent your json, as given below:
public class OstacolisRuntime
{
public int CodiceOstacolo { get; set; }
public int TipoOstacolo { get; set; }
public int Tipologia { get; set; }
public string Nome { get; set; }
public double PosizioneX { get; set; }
public double PosizioneY { get; set; }
public double PosizioneZ { get; set; }
public double AngoloX { get; set; }
public double AngoloY { get; set; }
public double AngoloZ { get; set; }
public double ScalaX { get; set; }
public double ScalaY { get; set; }
public double ScalaZ { get; set; }
public List<SubOggetto> SubOggettos { get; set; } //sub
}
public class SubOggetto
{
public string Immagine { get; set; }
public int Tipologia { get; set; }
public string Nome { get; set; }
public double PosizioneX { get; set; }
public double PosizioneY { get; set; }
public double PosizioneZ { get; set; }
public double AngoloX { get; set; }
public double AngoloY { get; set; }
public double AngoloZ { get; set; }
public double ScalaX { get; set; }
public double ScalaY { get; set; }
public double ScalaZ { get; set; }
public List<SubOggetto> SubOggettos { get; set; } //recursive relashioship
}
public class RootObject
{
public List<OstacolisRuntime> OstacolisRuntime { get; set; }
}
Deserialize you json:
var o= JsonConvert.DeserializeObject<RootObject>(json);
You can check complete source code
Related
I want to be able to save an arbitrary flat object into the name-value list.
public class NameValueListEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[InverseProperty(nameof(NameValueListContentEntity.Entity))]
public ICollection<NameValueListContentEntity> Content { get; set; }
}
public class NameValueListContent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("entity_fk")]
public NameValueListEntity Entity { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class ObjectToSave
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
I could use reflection to manually assemble/parse the list, but it will create a lot of overhead. Lots of NameValueListContent objects will be needlessly created both during the saving and the reading. Could it somehow be omitted? Especially during the reading, which is very performance-sensitive in my case.
Assume you have a AppDbContext class that holds your NameValueListContent class objects named as NVListContents. You can read and write the name-value list of objects by doing the following:
public class AppDbContext : DbContext
{
public DbSet<NameValueListContent> NVListContents { get; set; }
public AppDbContext()
: base()
{ }
}
public class SomeClass
{
private AppDbContext context { get; set; }
public SomeClass(AppDbContext _context)
{
context = _context;
}
public List<ObjectToSave> ReadObjects()
{
return context.NVListContents
.Select(nvlc => new ObjectToSave { Prop1 = nvlc.Name, Prop2 = nvlc.Value
}).ToList();
}
public bool WriteObjects(int id, string name, string value)
{
var query = context.NVListContents
.FirstOrDefault(nvlc => nvlc.Id == id);
if(query != null)
{
query.Name = name;
query.Value = value;
context.Update(query);
context.SaveChanges();
return true;
}
else
{
return false;
}
}
}
Hope, this answers to your question.
This small test here:
[TestClass]
public class TreeXTests
{
[TestMethod]
public void ShouldCreateSmallTree()
{
// Arrange
var fixture = new Fixture();
fixture.Behaviors.Add(new OmitOnRecursionBehavior(3));
fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
// Act
var em = fixture.Create<TreeX>();
// Assert
em.Should().NotBeNull();
}
}
Works fine when I have classes defined like this:
class TreeX
{
public MetaX MetaX { get; set; }
public IReadOnlyList<TreeX> Children { get; set; }
public TreeX OtherTreePointer { get; set; }
}
class MetaX
{
public int A { get; set; }
}
But when I add constructors and remove public setters:
class TreeX
{
public TreeX(MetaX metaX, IReadOnlyList<TreeX> children, TreeX otherTreePointer = null)
{
MetaX = metaX;
Children = children;
OtherTreePointer = otherTreePointer;
}
public MetaX MetaX { get; }
public IReadOnlyList<TreeX> Children { get; }
public TreeX OtherTreePointer { get; }
}
class MetaX
{
public MetaX(int a)
{
A = a;
}
public int A { get; }
}
It fails with the following error:
AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance from TreeX, most likely because it has no public constructor, is an abstract or non-public type.
Which is not really helpful.
Also I found that if I remove OtherTreePointer from TreeX then it works again:
class TreeX
{
public TreeX(MetaX metaX, IReadOnlyList<TreeX> children)
{
MetaX = metaX;
Children = children;
}
public MetaX MetaX { get; }
public IReadOnlyList<TreeX> Children { get; }
}
...but I really need OtherTreePointer. I don't want to redesign my classes and immutability is a must.
I'm not sure whether this is an issue with the library, but if not then how can I make it work?
Thanks.
I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.
I have a class like this:
public abstract class Node : Button
{
[XmlIgnoreAttribute()]
private bool isMovable;
public abstract ObjectType Type
{
get;
}
public double X { get; set; }
public double Y { get; set; }
public string Nodename { get; set; }
}
Serialization process:
ObjectXMLSerializer<List<Node>>.Save(main.current_data.Nodes, filename);
The trick happens when I try to serialize it: I don't want its parent's (Button) fields to be serialized, because this gives me serialization errors. So later, I can deserialize this xml to get an array of Nodes created when I read the fields they have.
Can I ignore the serialization of the parent's class somehow?
Thanks.
I'd go with containment instead. And serialize the contained NodeInfo. Node information would be the specific difference from a wpf button, the additional info you want to serialize.
public class ButtonNode : System.Windows.Controls.Button
{
private System.Windows.Controls.Button _button;
public ButtonNode(System.Windows.Controls.Button btn) : base() { this._button = btn; }
public NodeInfo NodeInfo { get; set; }
}
public interface INodeInfo { ObjectType Type { get; } }
[XmlInclude(typeof(ConcreteNodeInfo1))]
public abstract class NodeInfo : INodeInfo
{
public NodeInfo() { }
[XmlIgnore] private bool isMovable;
public abstract ObjectType Type { get; }
public double X { get; set; }
public double Y { get; set; }
public string NodeName { get; set; }
}
public class ConcreteNodeInfo1 : NodeInfo
{
public ConcreteNodeInfo1() : base () { }
public override ObjectType Type { get { return ObjectType.ObjectType1; }
}
As a side note, this post tackles the 'why shouldn't I use generics with XmlSerializer'.
I am new to serialization in general, and even newer to protobuf. Here is my problem, I have these classes:
[ProtoContract]
class Controle
{
[ProtoMember(1, AsReference=true)]
public HashSet<Controle> ControlesInternes { get; set; }
[ProtoMember(2)]
public string TypeControle { get; set; }
[ProtoMember(3)]
public Dictionary<string, string> Attributs { get; set; }
[ProtoMember(4)]
public int Ligne { get; set; }
[ProtoMember(5)]
public string InnerText { get; set; }
[ProtoMember(6)]
public Controle Parent { get; set; }
public Controle()
{
ControlesInternes = new HashSet<Controle>();
Attributs = new Dictionary<string, string>();
}
}
[ProtoContract(SkipConstructor=true)]
class PageAspx
{
[ProtoMember(1)]
public string PrefixeControleOnilait { get; set; }
[ProtoMember(2, AsReference = true)]
public HashSet<Controle> Controles { get; set; }
private string CheminTmp;
private string nomFichier;
[ProtoMember(3)]
public string NomFichier
{
get { return nomFichier; }
set { nomFichier = value; }
}
private string titre;
[ProtoMember(4)]
public string Titre
{
get { return titre; }
set { titre = value; }
}
public PageAspx()
{ }
public PageAspx(string pNomFichier)
{
this.NomFichier = pNomFichier;
this.Controles = new HashSet<Controle>();
}
}
When trying to serialize, I get a "possible recursion detected" error.
But basically, my code lists all controls in an aspx page, and they hierarchy (children, parents). That means that after my "PageAspx" object is made, it contains all the controls of the page, and for each of them, its parent and its children if it has any. When I don't serialize the member ControlesInternes, the serialization goes well. But I need this information.
How can I save these datas using protobuf?
I found a solution: I don't serialize the parents, and I use this function after deserialization in the "Controle" class:
[ProtoAfterDeserialization]
protected void OnDeserialized()
{
if (ControlesInternes.Count > 0)
{
foreach (var ctl in ControlesInternes)
{
ctl.Parent = this;
}
}
}