I have custom data type classes which I want .NET to convert to JSON (and later also back to CustomDataType).
I know that classes without any special definition will be converted to objects, simply by serializing all public properties. But this is not what I need.
Let's assume that I have a class
public class MyString : System.Object {
private string myString;
public MyString(string str) {
this.myString = str;
}
public override bool Equals(System.Object obj)
public override int GetHashCode()
public string ToString() {
return "!!!"+myString+"!!!";
}
}
Now, if I use this type in my ApiController
public class MyItem {
public MyString someStr;
}
public class MyApiController : ApiController {
[HttpGet]
public MyItem MyApi() {
MyItem item = new MyItem();
item.someStr = new MyString("I have a dream");
return item;
}
}
I get
{"someStr":{}}
but I may want to get
{"someStr":"!!!I have a dream!!!"}
or
{"someStr":{"words":4,"chars":11,"length":14}}
without actually exposing any properties as public.
How would I achieve that?
WebApi is using a Json serializer to get your result, when you're expecting it to execute a ToString() on it.
I personally haven't dealt with a lot of private variables (or properties) when serializing objects, however, it doesn't surprise me that it's doing this.
In order to get the desired result, you'll need to expose a property that returns !!!<whatever text>!!!.
Related
I have a class Response with generic parameter:
public class Response<T> where T : class {
public bool Result;
public T Data;
}
Also, I have a class Instance with simple parameters
public sealed class Instance {
public long Rank { get; set; }
public int ID_Member { get; set; }
}
And then I have a class where I use last ones
public sealed class InstanceResponse : Response<IList<Instance>> { }
And I try to add a constructor to last class and don't understand how to do it
I've tried like there, but it's doesn't work, JsonString contains serialized class InstanceResponse
public sealed class InstanceResponse : Response<IList<Instance>> {
public InstanceResponse(string JsonString) {
this = JsonConvert.DeserializeObject<InstanceResponse>(JsonString);
}
}
I've got an error Cannot assign to 'this' because it is read-only
How it possible?
It's not possible to deserialize json to the object and assign it directly in ctor to the object itself using this keyword.
Provided that
Json contains serialized class InstanceResponse
You can do something like this:
public sealed class InstanceResponse : Response<IList<Instance>> {
public InstanceResponse(string JsonString) {
var response = JsonConvert.DeserializeObject<InstanceResponse>(JsonString);
this.Data = response.Data;
this.Result = response.Result;
}
}
Another possible solution is to deserialize json in a code that creates instance of InstanceResponse (call's ctor) somewhere.
Instead of:
var response = new InstanceResponse(json);
You could deserialize json right there:
var response = JsonConvert.DeserializeObject<InstanceResponse>(json);
P.S.
With that being said, an interesting point was raised by #Lasse Vågsæther Karlsen regarding the subject. It is actually possible to assign something to this however it is only working inside of a structs ctor and use cases for it are very limited...(thanks Lasse)
Before I begin, I want to state I realize this isn't the ideal way of doing this. However the calling class can't be changed according to the rules of the assignment. I have tried to understand and find a solution to this problem, but I have had no luck.
Below there is 1 superclass,TreeMangement (There can only be 1 superclass for these subclasses). There are 3 subclasses(apple, orange and banana). The "find" method must be in the TreeMangement superclass. I am not allowed to override the "find" method. With the current code, I will get a casting error in the calling class. It will state that a TreeMangement can't implicity be casted into a AppleTree,OrangeTree or BananaTree.
Now my question is, am I able to somehow pass the correct type back to the calling class no matter what type (Apple,Banana,Orange) is calling it, without casting in the calling class? If so, how? If not, references so I know there is absolutely no way of doing it.
public class TreeMangement
{
public string id {get; set;}
public TreeMangement()
{
id = this.GetType().Name+"|"+Guid.NewGuid();
}
public static TreeMangement Find(string idIn)
{
string type = idIn.Split('|')[0];
return Functions.GetObj(idIn, GetFilePath(type), type); //returns back the right type
}
}
public class AppleTree:TreeMangement
{
public string Name;
}
public class OrangeTree:TreeMangement
{
public string Name;
}
public class BananaTree:TreeMangement
{
public string Name;
}
///////Calling class////
AppleTree savedAppleTree = AppleTree.Find("SomeValidID");
OrangeTree savedOrangeTree = OrangeTree.Find("SomeValidID");
BananaTree savedBananaTree = BananaTree.Find("SomeValidID");
You can change the superclass to a generic superclass like this:
public class TreeMangement<T>
where T: class
{
...
public static T Find(string idIn)
{
return ... as T;
}
}
Now you are able to specifiy the return type in your subclasses like
public class AppleTree:TreeMangement<AppleTree>
{
public string Name;
}
public class OrangeTree:TreeMangement<OrangeTree>
{
public string Name;
}
public class BananaTree:TreeMangement<BananaTree>
{
public string Name;
}
This way your 3 find calls will compile just fine as the Find() call will return the correct type:
var savedAppleTree = AppleTree.Find("SomeValidID");
var savedOrangeTree = OrangeTree.Find("SomeValidID");
var savedBananaTree = BananaTree.Find("SomeValidID");
Bofore I would like to ask my question, please read the follwing classes:
public class JsonPackage<A> : USOPackage
{
public JsonPackage(PackageHeader header, object o) : base(header, StringCompressor.CompressString(JsonConvert.SerializeObject(o, Formatting.Indented))) { }
public new A Content
{
get
{
return JsonConvert.DeserializeObject<A>(this.getContentAsString());
}
}
new public string getContentAsString()
{
return StringCompressor.DecompressString(base.getContentAsString());
}
}
(this class includes USOPackage(!))
and a second class:
public class LoginResponsePackage : JsonPackage<LoginResponse>
{
public LoginResponsePackage(LoginResult result) : base(PackageHeader.USO_AUTH_LOGIN_RESPONSE, new LoginResponse(result)) { }
public class LoginResponse
{
public LoginResult Result;
public LoginResponse(LoginResult r)
{
this.Result = r;
}
}
public enum LoginResult
{
OK,
FailedPassword,
FailedUsername,
FailedProtocolVersion
}
}
Now I would like to convert a "USOPackage" to an "LoginResponsePackage" to get the "Content" of the package in the right type.
If i do it like that:
JsonPackage<LoginResponsePackage.LoginResponse> responsePackage = (JsonPackage<LoginResponsePackage.LoginResponse>)usopackage;
So if I try to get "Content" it works which means i get a LoginResponse by calling
responsePackage.Content
but when i convert the class like that
LoginResponsePackage responsePackage = (LoginResponsePackage)usopackage;
I am not able to access the LoginReponse. It just give me access to for instance: BeginInvoke(), Method, Target, Clone()
I don't know where this comes from..
So my question is what do i have to change in my code to be ablt to directly cast the package so i get a "LoginResponse" by accessing "LoginResponsePackage.Content"
The problem is in this line of code:
public class LoginResponsePackage : JsonPackage<LoginResponse>
The type LoginResponse in this context doesn't mean the nested class you've defined in LoginResponsePackage, it refers to some type outside the class, I guess a delegate from your description of the methods there. You are not inside the class scope yet, so you have to refer to the nested type via its name you would use outside the class: LoginResponsePackage.LoginResponse.
The fixed line:
public class LoginResponsePackage : JsonPackage<LoginResponsePackage.LoginResponse>
I have my WCF service returning data both in XML and JSON format.
One functios has to return a List, because I don't know which class will be used to fill this list.
So, I have my class:
public class WrapHome
{
public WrapHome() { }
private string p_TITOLO { get; set; }
public string TITOLO { get { return p_TITOLO.ToString(); } set { p_TITOLO = value; } }
private List<object> p_CHART { get; set; }
public List<object> CHART { get { return p_CHART; } set { p_CHART = value; } }
}
and my WCF declaration:
[OperationContract]
[WebGet(UriTemplate = "datiHome.xml?token={token}&p1={p1}&p2={p2}", ResponseFormat = WebMessageFormat.Xml)]
List<WrapHome> GetDatiHomeXML(string token, string p1, string p2);
The output is correctly set, but, when it has to return it converted in XML (or JSON), it re-calls the method and finally give the err_connection_reset error.
I know the problem is the List, because if I comment it, it works. How can I use my List in my WCF output?
If you need more details, ask me without any problem.
You could define
[KnownType(typeof(MyChildObject0))]
...
[KnownType(typeof(MyChildObjectM))]
public class MyBaseObject { ... }
public class MyChildObject0 : MyBaseObject { ... }
...
public class MyChildObjectM : MyBaseObject { ... }
Or you could add the attribute only once and define static method that returns all M+1 types at once.
and modify:
public class WrapHome
{
...
public List<MyBaseObject> CHART { get;set; }
}
In my case the solution is more simple.
I have a class that I return in all my mehotds, like this:
[DataContract]
public class Result
{
[DataMember]
public string KeyMensaje;
[DataMember]
public string ErrorMessage;
[DataMember]
public object Objeto;
...
}
The problem is due to Objeto object inside this class. This object is used to return variables types and it can't be serialized if is a complex object, for example:
res = new Result(list, "ok", "", "", "", null);
return res;
In this case object (list) is a list of other custom object, for example List<CustomEmployee>
The solution is add on top Result class the complex types that object can be, like this:
[DataContract]
[KnownType(typeof(List<CustomEmployee>))]
[KnownType(typeof(CustomEmployee))]
public class Result
{
...
}
To make it work, your service has to know the types it needs to serialize. If they cannot be found out by looking at your method's signature (for example because one type is object), you need to list all types that could possibly be in that object.
Annotate them to the top of your service class, if you list for example can have instances of foo, bar and baz:
[ServiceKnownType(typeof(foo))]
[ServiceKnownType(typeof(bar))]
[ServiceKnownType(typeof(baz))]
I have a file that looks like the following:
public abstract class TestStep
{
public abstract bool DoWork();
public abstract List<TestStep> PrerequisiteSteps { get; set; }
public abstract string DisplayForm { get; }
}
class TestFunctions
{
public class A : TestStep
{
public override string DisplayForm { get { return "MainForm; } }
// remaining implementation goes here...
}
public class B : TestStep { // some implementation }
public class C : TestStep { // some implementation }
public static void NextStep() { }
}
I'd like to serialize the classes A, B, and C to an XML file. I can manually add instances of these classes to a List<TestStep> object and pass that to an XML serializer, but I'd like to programmatically accomplish this because I might add or remove classes in TestFunctions in the future. As a result, I've found that I can use reflection to get an array of the functions:
Type type = (typeof(TestEngineFunctions));
Type[] testEngineFunctions = type.GetNestedTypes(BindingFlags.Public);
However I'm not sure how to proceed from here. I have access to the name of the functions, I can get their properties as well, but ultimately I don't have an actual object to serialize.
Am I on the right track or is there another method better suited for this?
You can get a new instance of the objects like this:
ObjectType instance = (ObjectType)Activator.CreateInstance(objectType);
Since you may not know the ObjectType before run time you could use the dynamic type and don't cast:
dynamic instance = Activator.CreateInstance(objectType);
However, if you attempt to serialize right after you instantiate you'll just get the default values of the object in your XML.