0

With the following playload:

var playloads = new Dictionary<string, string>() {
{"FailMessage",
@"{
    ""success"": ""FALSE"",
    ""WsResult"": {
        ""result"": [],
        ""msg"": ""La r\u00e9f\u00e9rence ...""
    }
}"},
{"SuccessMessage", 
@"{
    ""success"": true,
    ""WsResult"": {
        ""result"": {
            ""FooNumber"": {
                ""Foo1"": {
                    ""Bar1"":""... il y a un probl\u00c3\u00a8me ."",
                    ""Bar2"":""bogus"",
                    ""Bar3"":""bogus""
                },
                ""Foo2"": {
                    ""Bar1"":""bogus"",
                    ""Bar2"":""bogus""
                },
            }
        },
        ""mssg"": """"
    }
}"} 
};

In this exemple Result switch from a Dictionary<string, Result> to an empty array when the Api has no result to serve. My original mapping class for sucessfull request was:

public class Foo1
{
    public string Bar1 { get; set; }
    public string Bar2 { get; set; }
    public string Bar3 { get; set; }
}

public class Foo2
{
    public string Bar1 { get; set; }
    public string Bar2 { get; set; }
}

public class Result
{
    public Foo1 Foo1 { get; set; }
    public Foo2 Foo2 { get; set; }
}

public class WsResult   {
    public Dictionary<string, Result> result { get; set; }
    public string msg { get; set; }
}

public class RootObject
{
    public string success { get; set; }
    public WsResult WsResult { get; set; }
}

While deserialising Good message works , bad message gave an error :

var resultA = JsonConvert.DeserializeObject<RootObject>(playloads["SuccessMessage"]);
var resultB = JsonConvert.DeserializeObject<RootObject>(playloads["FailMessage"]);

Cannot deserialize the current JSON array (e.g. [1,2,3]) into type Dictionary<string, Result>

So I try to add an property with this type but failed to make it work with the same property name:

//[JsonProperty("result")]
public List<object> Failresult { get; set; }

I am looking for a simple way to handle those case. The bigger picture is simple: Every response from the API has the same structure with a tricky property result in the middle that has a huge tentency to never return null or negative value (like a false etc) and will always return empty array.

Methode getSomething : Result is either a 'something' or an empty array.
Methode PostSomething : Result is either 1 or empty array.
Methode PostSomething2 : Result is either true or empty array.
Methode GetID : Result is either the int ID or empty array. if not found.
etc..

In Json.net is there a way to choose between property using their type?

Clarification:

It's not about the C# representation of a Json. It's more about mapping same property to different type. But it's not a duplicate of that. An Empty array of Nothing does not carry any informations. So there is no need to map it to anything. But you can just JsonIgnore it as it could be Something else than empty array and carry all the informations.

xdtTransform
  • 1,986
  • 14
  • 34
  • Does the API return the same HTTP status code for both success and fail cases? – Sergey Shevchenko Jan 11 '19 at 08:49
  • Yes but not the same string encoding. could be utf8, turk or 1152 . utf8 only happends in Failure message . But it's not enought. – xdtTransform Jan 11 '19 at 08:51
  • Vote to reopen it then, I still think you need to use the exact same solution though, with a converter, to handle this case, so I still think it is a valid duplicate. I'm not going to re-close it, however, if you or others vote to and successfully reopen it. But please look at the answer there using a converter to see if it solves your problem. – Lasse V. Karlsen Jan 11 '19 at 09:54
  • The problem is that if you're using json.net, it absolutely won't allow you to deserialize an array, even an empty one, into an object property (without a custom converter). The only way to get it to handle the case is to use a custom converter, to detect that it is an empty array and to return an empty object, or to detect that it is in fact an object, and do normal deserialization. Basically, I still feel that the only solution here is to use the converter, and thus the duplicate is still correct in my mind. – Lasse V. Karlsen Jan 11 '19 at 10:06
  • I understand that the correct *return* type from your method contains an object, and not the list, but you have to "trick" json.net into accepting your json in the first place. Isn't that what this question is about? – Lasse V. Karlsen Jan 11 '19 at 10:07
  • @lasse, First Im sorry once again I got lost in my So tab I travled to the wrong duplicate link. The dupe target is really close to what Im currently doing trying to fix the issue. Thanks for your time. It was a missunderstanding from my part. Great job, once again my appologies. – xdtTransform Jan 11 '19 at 10:17
  • No need to apologize, hope you manage to solve your problem. To be honest, I feel that api's that return this kind of json are broken by design, they should document what they return and then only return this but unfortunately I see this approach being used by many solutions. For instance, I've even seen a place that returned an array of objects if there were multiple, a single object (with no array) if there was just 1, and the literal `0` (probably "count"?) if there was nothing. It's a mess to handle. – Lasse V. Karlsen Jan 11 '19 at 10:21
  • @lass, it's broken to a point that it's a mental assault to the user. The return string change encoding for no objective Reason. The first property `success` is either a bool with value true or a string with capitilised 'FALSE'. property 'msg' is either msg or mssg, everything look handcrafted. – xdtTransform Jan 11 '19 at 10:23
  • I've seen `true` and `"FALSE"` as well, perhaps there is a library that "does it this way", whatever that means :) – Lasse V. Karlsen Jan 11 '19 at 10:48
  • Well I can't wait longer for it to be reopen to post the Custom `JsonConverter` . If it get re open : here is a past bin of the solution, anyone can post it I don't mind reputation and so just put the attribution. https://pastebin.com/UTr5FWmq . Dear Reviewer. remember that if all the answer does not Apply or if the answer doesnt not Apply to the dupe target it's not one ^^ . My answer will not be fit on the dupe target and the implementation and purpuse are really not the same. – xdtTransform Jan 11 '19 at 13:39
  • This is still a duplicate-- I just switched it to a more appropriate duplicate. – Brian Rogers Jan 11 '19 at 16:50

1 Answers1

0

Using this excellent site JSON2CSHARP, we can see that failure json generated those classes:

public class ObjetWSResult
{
    public List<object> result { get; set; }
    public string msg { get; set; }
}

public class RootObject
{
    public string success { get; set; }
    public ObjetWSResult objetWSResult { get; set; }
}

While successfull JSON generated:

public class Foo1
{
    public string Bar1 { get; set; }
    public string Bar2 { get; set; }
    public string Bar3 { get; set; }
}

public class Foo2
{
    public string Bar1 { get; set; }
    public string Bar2 { get; set; }
}

public class FooNumber
{
    public Foo1 Foo1 { get; set; }
    public Foo2 Foo2 { get; set; }
}

public class Result
{
    public FooNumber FooNumber { get; set; }
}

public class ObjetWSResult
{
    public Result result { get; set; }
    public string mssg { get; set; }
}

public class RootObject
{
    public bool success { get; set; }
    public ObjetWSResult objetWSResult { get; set; }
}

While RootObject is consistent, ObjectWSResult isn't, one has a List property and the other doesn't. So you can't use one mechanism to parse those JSONs.

What you can do is to try parse JSON assuming it is successfull, if it generates error, you would catch it and then you will parse it differently (to different class).

Michał Turczyn
  • 32,028
  • 14
  • 47
  • 69
  • I also have generated those class with Vs building method and have the same result. But Im not able to determine if the query will be a success or a Failure before deserialising the result. – xdtTransform Jan 11 '19 at 08:50
  • Sorry I didn't read the last lines of your answer. I assumed it was a balant Json2Csharp copy past. – xdtTransform Jan 11 '19 at 08:54
  • Im Editing the json 2 csharp , as this question is not a rampant duplicate on how to generate C# class from json. Already one deleted answer. – xdtTransform Jan 11 '19 at 08:57
  • @xdtTransform You're partially right, but I included generated classes, so answer is complete. Last lines are the way to go for you. – Michał Turczyn Jan 11 '19 at 09:04
  • Well every line of those generated class are in the question. But it's ok, I come back with a proper test of your solution. the issue is that I have this serialisation of the Json in a method, with a retrun type. So I kind of need to have one class. My try to work around for now is o have 2 property one for each type with different name and map this empty array result to a not use property and initialise the usefull one to the correct default value. – xdtTransform Jan 11 '19 at 09:28
  • @xdtTransform You can use `object` as a type for `result` in `ObjetWSResult`, then, accordingly to response, try to parse it to `List` or `Result`. This will allow you to have one class. – Michał Turczyn Jan 11 '19 at 09:32
  • While in `override object ReadJson` I can check the `Jtoken Type` and either ignore the token or populate the good field. `List` will force to either a Class to map it to the right type or AsType on every use. I will try to go further in the usage of it to see as it's your proposed solution. I must be missing something. but with light test I find it not perfect. The user of the method should not have to cast things from object – xdtTransform Jan 11 '19 at 09:40