I'm using Json.Net for Json deserialization. On occasion the Json string I read is not correct (which I can't fix since I don't produce it). In particular, at one specific place, where there should be a string, sometimes there is a serialized object instead. Json.Net then complains, not surprisingly, about finding an object where it expected a string.
I found I can intercept this by using Error
in JsonSerializerSettings
and also make Json.Net ignore the problem by setting ErrorContext.Handled
. But I want to do even more. If I could look at the serialised object I could figure out what the string should be and in theory supply the correct answer. In practice I can't figure our how to do so. Especially, in the error handler:
- How do I access the string that the parser tripped up on (Note that the parser can successfully continue if
ErrorContext.Handled
is set, so it can determine start and end of the problem string correctly)? - How do I supply the correct string back to the parser, or alternatively get access to the currently parsed object so I can set the value manually?
[Edit] As requested a simplified example:
The incorrect Json string I get to parse:
{
"id": 2623,
"name": {
"a": 39,
"b": 0.49053320637463277,
"c": "cai5z+A=",
"name": "22"
},
"children": [
{
"id": 3742,
"name": {
"a": 37,
"b": 0.19319664789046936,
"c": "Me/KKPY=",
"name": "50"
},
"children": [
{
"id": 1551,
"name": {
"a": 47,
"b": 0.6935373953047849,
"c": "qkGkMwY=",
"name": "9"
},
"children": []
},
{
"id": 4087,
"name": {
"a": 5,
"b": 0.42905938319352427,
"c": "VQ+yH6o=",
"name": "84"
},
"children": []
},
{
"id": 614,
"name": {
"a": 19,
"b": 0.7610801005554758,
"c": "czjTK1s=",
"name": "11"
},
"children": []
}
]
},
{
"id": 3382,
"name": {
"a": 9,
"b": 0.36416331043660793,
"c": "lnoHrd0=",
"name": "59"
},
"children": [
{
"id": 4354,
"name": {
"a": 17,
"b": 0.8741648112769075,
"c": "CD2i2I0=",
"name": "24"
},
"children": []
},
{
"id": 2533,
"name": {
"a": 52,
"b": 0.8839575992356788,
"c": "BxFEzVI=",
"name": "60"
},
"children": []
},
{
"id": 5733,
"name": {
"a": 4,
"b": 0.7230552787534219,
"c": "Un7lJGM=",
"name": "30"
},
"children": []
}
]
},
{
"id": 9614,
"name": {
"a": 81,
"b": 0.4015882813379114,
"c": "dKgyRZk=",
"name": "63"
},
"children": [
{
"id": 7831,
"name": {
"a": 81,
"b": 0.2784254314743101,
"c": "xZur64o=",
"name": "94"
},
"children": []
},
{
"id": 6293,
"name": {
"a": 73,
"b": 0.32629523068959604,
"c": "lMkosP4=",
"name": "93"
},
"children": []
},
{
"id": 5253,
"name": {
"a": 13,
"b": 0.19240453242901923,
"c": "oOPZ3tA=",
"name": "5"
},
"children": []
}
]
}
]
}
And here to class to parse it into:
class Node
{
[JsonProperty]
int id;
[JsonProperty]
string name;
[JsonProperty]
List<Node> children;
}
As you can see it expects a string name
but sometimes incorrectly gets a serialised object (which contains the string in question as a member). This only happens in some JSON strings but not others, so I can't just change the class definition of Node to match.
[Edit 2] A "correct" Json string according to my API would look like this:
{
"id": 2623,
"name": "22",
"children": [
{
"id": 3742,
"name": "50",
"children": [
{
"id": 1551,
"name": "9",
"children": []
},
{
"id": 4087,
"name":"84",
"children": []
},
...
Trying to detect errors after the fact and then reparse from the point of the error is going to be problematic, as you have seen. Fortunately, the problem you've described can be solved in a straightforward manner using a custom
JsonConverter
. The idea is to have the converter read the data into a temporary structure that can handle either form (object or string), query the type, then construct theNode
from there.Here is the code for the converter:
To use the converter, add a
[JsonConverter]
attribute to yourNode
class like this:Then you can deserialize as normal:
Here is a full demo showing the converter in action. For illustration purposes, I've created a new JSON string that contains a combination of the "good" and "bad" nodes you described in your question.
Output: