Protobuf-net possible recursion detected: serialize children and parents - c#

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;
}
}
}

Related

Project a name-value list into an object

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.

Trying to work out these interfaces

I'm trying to create some interfaces. The IReportSection object will have one string and a collection of items, which could be different depending on what we're working with. Do I need to make it generic?
The IReport will have one string and a collection of IReportSection.
Here's how I'm trying to define it now.
public interface IReport
{
string ReportName { get; set; }
ICollection<IReportSection> ReportSections { get; }
}
public interface IReportSection
{
string ReportSectionName { get; set; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection ReportItems { get; set; }
}
And my models:
pulic class ProjectSubmissionViewModel
{
public int ProjectSubmissionId { get; set; }
public string SubmissionTitle { get; set; }
}
pulic class AffiliateViewModel
{
public int AffiliateId { get; set; }
public string AffiliateName { get; set; }
}
This is how I'm trying to use it in code:
public class ChapterAffiliates : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Affiliates"; } }
public ICollection<AffiliateViewModel> ReportItems { get; set; }
}
public class ChapterTitles : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Titles"; } }
public ICollection<ProjectSubmissionViewModel> ReportItems { get; set; }
}
public class SubmissionListViewModel : IReport
{
public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }
public ICollection<AffiliateViewModel> Affiliates{ get; set; }
public string ReportName { get; set; }
public ICollection<IReportSection> ReportSections
{
get
{
var affiliateSection = new ChapterAffiliates
{
ReportItems = Affiliates
};
var titleSection = new ChapterTitles
{
ReportItems = Submissions.Where(s => s.SubmissionTitle.Contains("SomePhrase")).ToList()
};
var sections = new List<IReportSection> { {subSection}, {titleSection} };
return sections;
}
}
}
I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.
Are the type parameters for TRType all the same within a certain report? E.g. will you have report sections with different report types in them?
If all types within a report are the same, the solution is relatively simple:
public interface IReport<T> { ... }
If this is not the case - you'll have to do something different, e.g:
public interface IReportSection
{
string ReportSectionName { get; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase<TRType> : IReportSection {
...
}
This allows you to put different underlying types in the ReportSections collection related to the report. You'll have to do some more work to get the exact information that you need out of each report section.

JSON to C# object

I have a problem with JSON.
"{"status":"ok","message":"Dane klienta zostau0142y pobrane pomyu015blnie","clientData":
{"id":22,"imie":"Pppppppppp","nazwisko":"Ppppppppppppp","tel":"111111126","email":"aaa#a.pl","ulica":"Na Przyzbie","nr_budynku":"3","nr_lokalu":"41","kod_pocztowy":"02-813","miejscowosc":"Warszawa","samochod_marka":"opel","samochod_model":"vectra","subcategories":
{"6":200}}}"
and it's my class
public class Client
{
public string Status { get; set; }
public string Message { get; set; }
public Data clientData { get; set; }
}
public class Data
{
public Dictionary<string, string> clientData { get; set; }
}
everything is mostly correct but when I debug my code field clientData is null.
What am I doing wrong?
Thanks for help!
EDIT:
it's how I deserialize object.
var myObject = JsonConvert.DeserializeObject<Client>(get_person);
The problem with your current attempt is that you are trying to convert clientData to a Dictionary<string, string>. This is causing an issue because not all of your values are strings, the problematic ones are as follows:
id : int
subcategories : Dictionary<string, int>
If you don't want to explicitly define all of your properties due to them changing without notice, then I would recommend a change to your JSON structure as follows:
{
"status": "ok",
"message": "Dane klienta zostau0142y pobrane pomyu015blnie",
"clientData": {
"id": 22,
"properties": {
"imie": "Pppppppppp",
"nazwisko": "Ppppppppppppp",
"tel": "111111126",
"email": "aaa#a.pl",
"ulica": "Na Przyzbie",
"nr_budynku": "3",
"nr_lokalu": "41",
"kod_pocztowy": "02-813",
"miejscowosc": "Warszawa",
"samochod_marka": "opel",
"samochod_model": "vectra"
},
"subcategories": {
"6": 200
}
}
}
Then you change your C# class structure to the following:
public class Client
{
public string Status { get; set; }
public string Message { get; set; }
public Data clientData { get; set; }
}
public class Data
{
public int id { get; set;}
public Dictionary<string, string> properties { get; set; }
public Dictionary<string, int> subcategories { get; set; }
}
That should work (though I haven't tested), and will hopefully allow you to use it how you need to still.
NOTE: You could also move id and subcategories into the root, and keep clientData as a Dictionary<string, string>. All depends on your preference really, the important thing here is that you be careful not to mix types.
Json
{
"status":"ok",
"message":"Dane klienta zostau0142y pobrane pomyu015blnie",
"clientData":{
"id":22,
"imie":"Pppppppppp",
"nazwisko":"Ppppppppppppp",
"tel":"111111126",
"email":"aaa#a.pl",
"ulica":"Na Przyzbie",
"nr_budynku":"3",
"nr_lokalu":"41",
"kod_pocztowy":"02-813",
"miejscowosc":"Warszawa",
"samochod_marka":"opel",
"samochod_model":"vectra",
"subcategories":{
"6":200
}
}
}
C# classes
public class Subcategories
{
public int __invalid_name__6 { get; set; }
}
public class ClientData
{
public int id { get; set; }
public string imie { get; set; }
public string nazwisko { get; set; }
public string tel { get; set; }
public string email { get; set; }
public string ulica { get; set; }
public string nr_budynku { get; set; }
public string nr_lokalu { get; set; }
public string kod_pocztowy { get; set; }
public string miejscowosc { get; set; }
public string samochod_marka { get; set; }
public string samochod_model { get; set; }
public Subcategories subcategories { get; set; }
}
public class RootObject
{
public string status { get; set; }
public string message { get; set; }
public ClientData clientData { get; set; }
}
Note that root->clientData->subcategories->6 would result in invalid class name, as class names in C# can not begin with a number.
With hack fix:
For example:
public class DynamicDictionary : DynamicObject
{
private readonly Dictionary<string, object> dictionary;
public DynamicDictionary(Dictionary<string, object> dictionary)
{
this.dictionary = dictionary;
}
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
return dictionary.TryGetValue(binder.Name, out result);
}
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
dictionary[binder.Name] = value;
return true;
}
}
Which can be used as follows:
dynamic x = new DynamicDictionary(
new Dictionary<string, object> {{"Name", "Peter"}});
you can use Newtonsoft.Json - add a reference to your project and add the using directive
using Newtonsoft.Json;
//then your code
dynamic ma_json = JsonConvert.DeserializeObject<dynamic>(json);
//and then you can get say the id:
var id = ma_json.clientData.id;
// ... do whatever you want with the id
if (ma_json.clientData.id == 22) //evaluates to true in your case
{
//do something
}

Serialize deserialize anonymous child JSON properties to model

I have an API I am receiving data from. That API is out of my control on how it is structured, and I need to serialize and deserialize the JSON output to map the data to my model.
Everything works well where JSON is nicely formatted with named properties.
What can you do where there is no named value and there is just an array of ints and strings? like under locations
here is a sample of the JSON:
{"id":"2160336","activation_date":"2013-08-01","expiration_date":"2013-08-29","title":"Practice Manager","locations":{"103":"Cambridge","107":"London"}}
I have models that are like:
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date{ get; set; }
public string Title { get; set; }
public Location Locations { get; set; }
}
public class Location
{
public int Id { get; set; }
public string value { get; set; }
}
and I am mapping using the inbuilt ajax serialization:
protected T MapRawApiResponseTo<T>( string response )
{
if ( string.IsNullOrEmpty( response ) )
{
return default( T );
}
var serialize = new JavaScriptSerializer();
return serialize.Deserialize<T>( response );
}
var results = MapRawApiResponseTo<ItemResults>(rawApiResponse);
So the ID and all other properties are picked up and mapped but what every I do I can not seem to map the locations.
Many thanks
public Dictionary<int,string> Locations { get; set; }
job done; you should find that using Json.NET, at least, i.e.
var result = JsonConvert.DeserializeObject<ItemResults>(json);
you get 2 entries in result.Locations; specifically result[103] = "Cambridge"; and result[107] = "London";
If you don't mind, you can workaround with dictionary:
class Program
{
static void Main(string[] args)
{
string json =
"{'id':'2160336','activation_date':'2013-08-01','expiration_date':'2013-08-29','title':'Practice Manager','locations':{'103':'Cambridge','107':'London'}}";
var deserializeObject = JsonConvert.DeserializeObject<ItemResults>(json);
Console.WriteLine("{0}:{1}", deserializeObject.Locations.First().Key, deserializeObject.Locations.First().Value);
Console.ReadKey();
}
}
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date { get; set; }
public string Title { get; set; }
public Dictionary<int, string> Locations { get; set; }
}
you can also use manual parsing, like here: Json.NET (Newtonsoft.Json) - Two 'properties' with same name?
This will work:
public Dictionary<string, string> Locations { get; set; }
public IEnumerable<Location> LocationObjects { get { return Locations
.Select(x => new Location { Id = int.Parse(x.Key), value = x.Value }); } }
I propose you the following solution :
public class ItemResults
{
public int Id { get; set; }
public DateTime Activation_Date { get; set; }
public DateTime Expiration_Date { get; set; }
public string Title { get; set; }
[JsonProperty("locations")]
public JObject JsonLocations { get; set; }
[JsonIgnore]
public List<Location> Locations { get; set; }
[OnDeserialized]
public void OnDeserializedMethod(StreamingContext context)
{
this.Locations = new List<Location>();
foreach (KeyValuePair<string, JToken> item in this.JsonLocations)
{
this.Locations.Add(new Location() { Id = int.Parse(item.Key), value = item.Value.ToString() });
}
}
}
public class Location
{
public int Id { get; set; }
public string value { get; set; }
}
After you just have to deserialize your JSON with : JsonConvert.DeserializeObject<ItemResults>(json);

Inconsistent Accessibility: Property Type Error

I've done the googling to no avail. This is the one sole error preventing my code from compiling and running but I can't seem to figure it out. The exact text of the error is "...Dictionary is less accessible than property FleetAirliner.InsuranceProperties"
Any ideas what could be causing this?
namespace TheAirline.Model.AirlinerModel
{
[Serializable]
public class FleetAirliner
{
public Airliner Airliner { get; set; }
public string Name { get; set; }
public Airport Homebase { get; set; }
public enum PurchasedType { Bought, Leased,BoughtDownPayment }
public DateTime PurchasedDate { get; set; }
public PurchasedType Purchased { get; set; }
public Boolean HasRoute { get { return this.Routes.Count > 0; } set { ;} }
public AirlinerStatistics Statistics { get; set; }
/*Changed for deleting routeairliner*/
public enum AirlinerStatus { Stopped, On_route, On_service, Resting, To_homebase, To_route_start }
public AirlinerStatus Status { get; set; }
public Coordinates CurrentPosition { get; set; }
public List<Route> Routes { get; private set; }
public Flight CurrentFlight { get; set; }
public DateTime GroundedToDate { get; set; }
public List<Pilot> Pilots { get; set; }
public Dictionary<string, AirlinerInsurance> InsurancePolicies { get; set; } //error occurs here
public int NumberOfPilots {get {return this.Pilots.Count;} private set {;}}
public FleetAirliner(PurchasedType purchased,DateTime purchasedDate, Airline airline,Airliner airliner,Airport homebase)
{
this.Airliner = airliner;
this.Purchased = purchased;
this.PurchasedDate = purchasedDate;
this.Airliner.Airline = airline;
this.Homebase = homebase;
this.Name = airliner.TailNumber;
this.Statistics = new AirlinerStatistics(this);
this.Status = AirlinerStatus.Stopped;
this.CurrentPosition = new Coordinates(this.Homebase.Profile.Coordinates.Latitude, this.Homebase.Profile.Coordinates.Longitude);
this.Routes = new List<Route>();
this.Pilots = new List<Pilot>();
this.InsurancePolicies = new Dictionary<string, AirlinerInsurance>();
}
It means that class "AirlinerInsurance" Is not Public.
It is a property that is public, but other classes, that are allowed to use the property, might not have access rights to the class itself (it is private / internal).
Edit
Now that you have posted the code of class "AirlinerInsurance", just add a "public" modifier to it.
You can read more about it here and here
you need
class AirlinerInsurance {
// stuff
}
to be
public class AirlinerInsurance {
//stuff
}

Categories

Resources