I am using Newtonsoft.Json .Net for 4.0 for this project
Parent class:
public class CLiveThing
{
private object lawk = new object();
public Action<double> hp_cur_changed;
public Action<double> hp_max_changed;
public double hp_max { get; private set; }
public double hp_cur { get; private set; }
public void change_hp_max(double val)
{
lock (lawk)
{
hp_max += val;
if (hp_max_changed != null)
hp_max_changed(hp_max);
}
}
public void change_hp_cur(double val)
{
lock (lawk)
{
hp_cur += val;
if (hp_cur_changed != null)
hp_cur_changed(hp_cur);
}
}
}
Child class:
public class CPlayer : CLiveThing
{
public int id { get; private set; }
public CPlayer(int id)
{
this.id = id;
}
/*
* Network
*/
public string Serialize()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this);
}
public static CPlayer Deserialize(string val)
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<CPlayer>(val);
}
}
Server (uses Players.CPlayers to manage all players with a generic collection)
Players.CPlayers.Serialize()
Players.CPlayers.Serialize serializes all players in the server's memory, one per line
Like so:
public static string Serialize()
{
players_lock.AcquireReaderLock(Timeout.Infinite);
string str = "";
foreach (CPlayer player in players.Values)
{
str += player.Serialize();
str += Environment.NewLine;
}
players_lock.ReleaseReaderLock();
return str;
}
Client
I put a break line in the Players.CPlayers.Deserialize loop, which reverses what the server did.
foreach (string line in split)
{
if (line.Length > 0)
{
CPlayer player = CPlayer.Deserialize(line);
addOrReplace(player.id, player);
}
}
Here's an example of one line:
What goes in:
"{\"hp_cur_changed\":null,\"hp_max_changed\":null,\"id\":1,\"hp_max\":100.0,\"hp_cur\":100.0}"
What comes out of CPlayer.Deserialize():
It only Deserialized the ID and ignored the properties in the parent class. Which is weird because the server-side did Serialize it properly. Can anyone tell me how to fix this?
I was not able to find an official reference why it's working like this but there are at least two way to solve your problem:
Declare your base class property setters as public
public double hp_cur { get; set; }
public double hp_max { get; set; }
Or annotate them with the JsonProperty attribute:
[JsonProperty]
public double hp_max { get; private set; }
[JsonProperty]
public double hp_cur { get; private set; }
Related
I have been stuck for days because of this problem, let me explain, I have the following classes:
In the entity layer:
[JsonObject(MemberSerialization.OptIn)]
public class telephone__entity
{
[JsonProperty("id")] internal int _id;
[JsonProperty("number")] internal string _number;
public int id { get; set; }
public string number { get; set; }
}
[JsonObject(MemberSerialization.OptIn)]
public class autor__entity
{
[JsonProperty("id")] internal int _id;
[JsonProperty("name")] internal string _name;
[JsonProperty("telephones")] internal BindingList<telephone__entity> _telephones;
public int id { get; set; }
public string name { get; set; }
public BindingList<telephone__entity> telephones { get; set; }
public autor__entity()
{
_telephones = new BindingList<telephone__entity>();
telephones.ListChanged += telephones_ListChanged;
}
private void telephones_ListChanged(object sender, ListChangedEventArgs e)
{
// VALIDATIONS AND CUSTOM OPERATIOS
}
}
[JsonObject(MemberSerialization.OptIn)]
public class book__entity
{
[JsonProperty("title")] internal string _title;
[JsonProperty("autors")] internal BindingList<autor__entity> _autors;
public string titulo { get; set; }
public BindingList<autor__entity> autors { get; set; }
public book__entity()
{
_autors = new BindingList<autor__entity>();
autors.ListChanged += autors_ListChanged;
}
private void autors_ListChanged(object sender, ListChangedEventArgs e)
{
// VALIDATIONS AND CUSTOM OPERATIOS
}
}
[JsonObject(MemberSerialization.OptIn)]
public class library__entity
{
[JsonProperty("id")] internal int _id;
[JsonProperty("books")] internal BindingList<book__entity> _books;
public int id { get; set; }
public BindingList<book__entity> books { get; set; }
public library__entity()
{
_books = new BindingList<book__entity>();
books.ListChanged += books_ListChanged;
}
private void books_ListChanged(object sender, ListChangedEventArgs e)
{
// VALIDATIONS AND CUSTOM OPERATIOS
}
}
In the same layer I have a class that adds the AddRange functionality BindingList type (I will use this functionality in the data layer):
public static class BindingListExtensions
{
public static void AddRange<T>(this System.ComponentModel.BindingList<T> bindingList, IEnumerable<T> collection, bool raise_events_enabled = false)
{
if (collection != null)
{
var oldRaiseEventsValue = bindingList.RaiseListChangedEvents;
try
{
bindingList.RaiseListChangedEvents = false;
foreach (var value in collection)
{
bindingList.Add(value);
}
}
finally
{
bindingList.RaiseListChangedEvents = oldRaiseEventsValue;
if (raise_events_enabled && bindingList.RaiseListChangedEvents)
{
bindingList.ResetBindings();
}
}
}
}
}
Now in the data layer, I have a method that receives a parameter of type IDataReader to then perform the cast accordingly.
using (var reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
var record = new library__entity();
{
record._id = Convert.ToInt32(reader["ID"]);
record._books.AddRange(JsonConvert.DeserializeObject<book__entity>(Convert.ToString(reader["BOOKS"]))); // THIS IS WHERE I HAVE THE ERROR, BECAUSE THE TELEPHONES_LISTCHANGED EVENT OF THE AUTOR__ENTITY CLASS IS BEING FIRED
// 1) CONVERT.TOSTRING(READER["BOOKS"]) GETS A JSON STRING FROM THE DATABASE
// 2) I AM USING RECORD._BOOKS.ADDRANGE TO PREVENT THE BOOKS_LISTCHANGED EVENT FROM FIRING
// 3) I GUESS THE PROBLEM WITH USING JSONCONVERT.DESERIALIZEOBJECT<BOOK__ENTITY> IS THAT THE USE OF ADDRANGE IS OMITTED. ANY IDEAS HOW I CAN FIX THIS?
}
return record;
}
}
Thanks.
I have a POCO like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
Now when I serialize my POCO I get json back like this which has field name:
{"LCode":"en-US","Currency":"USD","CCode":"IN"}
Is there any way to get it the way DataMember fields are after serializing POCO. Something like below:
{"lang_code":"en-US","data_currency":"USD","country_code":"IN"}
Below is the code we have:
ProcessStr = ExtractHeader(headers, PROCESS_HEADER);
Console.WriteLine(ProcessStr);
if (!string.IsNullOrWhiteSpace(ProcessStr))
{
Process = DeserializeJson<Process>(ProcessStr);
if (Process != null && !string.IsNullOrWhiteSpace(Process.Gold))
{
Process.Gold = HttpUtility.HtmlEncode(Process.Gold);
}
ProcessStr = Process.ToString();
Console.WriteLine(ProcessStr);
}
private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
It looks like you are using two different packages, Newtonsoft.Json to serialize and Utf8Json to deserialize. They use different annotations. You can get it to work, but it might be simpler to choose one or the other.
Newtonsoft.Json uses the JsonProperty attribute whereas Utf8Json uses the DataMember one.
using System;
using System.Diagnostics;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Utf8Json;
namespace JSONPropertyTest
{
public class Process
{
public Process() { }
[JsonProperty("lang_code")]
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[JsonProperty("data_currency")]
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[JsonProperty("country_code")]
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
class Program
{
static private T DeserializeJson<T>(string str) where T : new()
{
try
{
return Utf8Json.JsonSerializer.Deserialize<T>(str);
}
catch (Exception e)
{
return new T();
}
}
static void Main(string[] args)
{
var test = new Process { LCode = "en-US",Currency = "USD", CCode = "IN" };
var json = test.ToString();
Console.WriteLine($"serialized={test}");
var deserialized = DeserializeJson<Process>(json);
Debug.Assert(test.CCode == deserialized.CCode);
Debug.Assert(test.LCode == deserialized.LCode);
Debug.Assert(test.Currency == deserialized.Currency);
Console.WriteLine($"deserialized={deserialized}");
}
}
}
To just use Utf8Json you need to update your ToString method, which is the only one in the code you've shown that relies on Newtonsoft.Json. That would look like this:
public class Process
{
public Process() { }
[DataMember(Name = "lang_code")]
public string LCode { get; set; }
[DataMember(Name = "data_currency")]
public string Currency { get; set; }
[DataMember(Name = "country_code")]
public string CCode { get; set; }
public override string ToString()
{
return Utf8Json.JsonSerializer.ToJsonString(this);
}
}
I want to call a method for my WPF-App with subtype objects of my Piece class. My problem is that the subtype objects have more properties than e.g the the Text objects.
Do you know a way to cope with this better than I do in my FillForm example?
namespace Namespace
{
public abstract class Piece
{
public int id { get; set; }
public string title { get; set; }
public string description { get; set; }
}
public class Text : Piece
{
}
public class Image: Piece{
public string filePath { get; set; }
public string fileformat { get; set; }
}
public class Video : Image
{
}
}
}
Example method:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (!currentPiece.GetType().ToString().Equals("Namespace.Text"))
{
pieceFileSelectURLTextBlock.Text = (currentPiece as Namespace.Image).filePath;
SetPreviews((currentPiece as Namespace.Image).filePath);
}
}
Thanks!
Why not just change the method to the following with more type-safety
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
if (currentPiece as Namespace.Image imagePiece)
{
pieceFileSelectURLTextBlock.Text = imagePiece.filePath;
SetPreviews(imagePiece.filePath);
}
}
Do a safecast:
public void FillForm(Piece currentPiece)
{
pieceIdTextBox.Text = currentPiece.id.ToString();
pieceNameTextBox.Text = currentPiece.title;
pieceDescriptionTextBox.Text = currentPiece.description;
var imagePiece = currentPiece as Image;
if(imagePiece != null)
pieceFileSelectURLTextBlock.Text = imagePiece .filePath;
SetPreviews(imagePiece .filePath);
}
}
Iam implemented a webservice using c# webapi,but my json response array is empty.
My code
public object Post([FromBody] castdet castdet1)
{
mid = castdet1.mid1;
return Request.CreateResponse(jsonvalues(mid));
}
private object jsonvalues(string mid)
{
DataTable dtalcast = GetAllcast();
foreach (DataRow drow in dtalcast.Rows)
{
string mouvieid = drow["MovieMasterId"].ToString();
string actname = drow["ActorName"].ToString();
string charname = drow["CharacterName"].ToString();
if (mouvieid == mid)
{
temp = 1;
castdet.Add(new myobject(actname, charname));
}
}
return castdet;
}
public class castdet
{
public string mid1 { get; set; }
}
public class myobject
{
string actorname;
string charactername;
public myobject(string v1, string v2)
{
actorname = v1;
charactername = v2;
}
}
My json response string is like this [{},{}],its empty.What went wrong for me?
You need to make the fields of myobject be public. Or better yet make them be public properties:
public class myobject
{
public string actorname { get; set; }
public string charactername { get; set; }
public myobject(string actorname, string charactername)
{
this.actorname = actorname;
this.charactername = charactername;
}
}
(You may have other problems - your code is incomplete and does not compile. Also, you should modify your methods to explicitly return the actual types being returned, not just to return object. This allows for compile-time checking for type errors.)
Imagine a class as follows.. It's a class provided to me to work with.. I cannot change its source..
public class MyClass
{
object _Object { get; set; }
public void FuncA1() { _Object = new object(); }
public void FuncA2() { _Object = new List<object>(); }
public int FuncB1() { _Object = 0; return 0; }
public int FuncB2() { _Object = 123; return 123; }
public string FuncC1() { _Object = null; return null; }
public string FuncC2() { _Object = "Hello"; return "Hello"; }
}
Im trying to create a wrapper for this class, such that I can group its many functions into categories..
MyWrapper.Voids.FuncA1();
MyWrapper.Voids.FuncA2();
MyWrapper.Integers.FuncB1();
MyWrapper.Integers.FuncB2();
MyWrapper.Strings.FuncC1();
MyWrapper.Strings.FuncC2();
The only solution I can think of for this scenario is to design the wrapper like this:
public class MyWrapper
{
MyClass _Instance { get; set; }
public _Void Voids { get; private set; }
public _Integer Integers { get; private set; }
public _String Strings { get; private set; }
public class _Void
{
MyWrapper _Parent { get; set; }
public void FuncA1() { _Parent._Instance.FuncA1(); }
public int FuncA2() { return _Parent._Instance.FuncA2(); }
}
public class _Integer
{
...
}
public class _String
{
...
}
public MyWrapper()
{
_Instance = new MyClass();
Voids = new _Voids(this);
Integers = new _Integer(this);
Strings = new _String(this);
}
}
This solution works, but has a number of problems:
- The inner classes are forced to be public, which allows them to be instantiated by the user..
- I am forced to maintain a reference of the parent object in the child classes..
Is there a better way of doing this?
EDIT: The code posted initially was a bit confusing, in the sense that it was diverting attention away from the core issue and more into the issues of whether a function would cause exceptions or not if they all work on the same object..
NOTE: This is not actual code.. I hacked together this example to show what I'm trying to do.. CREATE A WRAPPER AROUND AN OBJECT (I cannot change the original object's code) AND GROUP FUNCTIONS INTO CATEGORIES..
FINAL EDIT: following suggestion by Juharr.. here's what ive done to accomplish what i wanted.. for the betterment of others..
public interface IVoid
{
void FuncA1();
void FuncA2();
}
public interface IInteger
{
int FuncB1();
int FuncB2();
}
public class MyWrapper
{
public MyClass Instance { get; private set; }
public IVoid Voids { get; private set; }
public IInteger Integers { get; private set; }
private abstract class MyBase
{
protected MyWrapper Parent { get; set; }
protected MyClass Instance { get { return Parent.Instance; } }
public MyBase(MyWrapper oParent) { Parent = oParent; }
}
private class MyVoid : MyBase, IVoid
{
public MyVoids (MyWrapper oParent) : base(oParent) { }
public void FuncA1() { Instance.FuncA1(); }
public void FuncA2() { Instance.FuncA2(); }
}
private class MyInteger : MyBase, IInteger
{
public MyInteger (MyWrapper oParent) : base(oParent) { }
public int FuncB1() { return Instance.FuncB1(); }
public int FuncB2() { return Instance.FuncB2(); }
}
public MyWrapper()
{
Instance = new MyClass();
Voids = new MyVoid(this);
Integers = new MyInteger(this);
}
}
You could write public interfaces instead. Then your inner classes don't have to be public. So something like this.
public interface IIntger
{
void Set(int iValue);
int Get();
}
public class MyWrapper
{
MyClass _Instance { get; set; }
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyWrapper _Parent { get; set; }
public void Set(int iValue) { _Parent._Instance.IntegerSet(iValue); }
public int Get() { return _Parent._Instance.IntegerGet(); }
}
public MyWrapper()
{
_Instance = new MyClass();
Integer = new _Integer(this);
}
}
EDIT:
To answer the second part of your question you will either need the reference to the parent class or a reference to the class you are wrapping. So you could have this instead.
public class MyWrapper
{
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyClass _Instance { get; set; }
public _Integer(MyClass myClass) { _Instance = myClass; }
public void Set(int iValue) { _Instance.IntegerSet(iValue); }
public int Get() { return _Instance.IntegerGet(); }
}
public MyWrapper(MyClass instance)
{
Integer = new _Integer(instance);
}
}