简体   繁体   中英

c# - Deserialize a Json in two steps

I would like to deserialize my Json in two step because I have to read the first part to know what kind of Object it is.

I have to read this kind of Json :

{"header":3,"data":{"result":"myResult"}}

it's more readable like that

{
    "header":3,
    "data":{
        "result":"myResult"
    }
}

I deserialize this Json in a class named ProtocolHeader :

public class ProtocolHeader
{
    [JsonProperty("header")]
    public int Header { get; set; }

    [JsonProperty("data")]
    public string Data { get; set; }
}

To do this I use this code :

JsonConvert.DeserializeObject<ProtocolHeader>(Json)

Depending on the value of the Header, I will choose different class to deserialize the end of the file.

For example, I could have another class

public class ProtocolResult
{
    [JsonProperty("result")]
    public string Result{ get; set; }
}

or like that

public class ProtocolError
{
    [JsonProperty("errorNumber")]
    public int ErrorNumber{ get; set; }

    [JsonProperty("additionalInformation")]
    public string AdditionalInformation{ get; set; }
}

Do you have an idea to seperate the Deserialize Object in two steps ?

Thanks

You could make 3 classes. One common class (not base) which has all of the fields, then a ProtocolResult & ProtocolError

Then implement an implicit cast to each. You could also put a IsError getter on to your common class to decide how to use it.

You can use a reader to only read as long as you need, then skip out of the reader and do your real deserialization.

Probably not a whole lot better than deserializing into a simple object first then a real object later, but it's an alternative.

You can probably tweak this a bit.

string json = @"{""header"":3,""data"":{""result"":""myResult""}}";

using (var stringReader = new StringReader(json))
{
    using (var jsonReader = new JsonTextReader(stringReader))
    {
        while (jsonReader.Read())
        {
            if (jsonReader.TokenType == JsonToken.PropertyName 
                && jsonReader.Value != null
                && jsonReader.Value.ToString() == "header")
            {
                jsonReader.Read();
                int header = Convert.ToInt32(jsonReader.Value);
                switch (header)
                {
                    case 1:
                        // Deserialize as type 1
                        break;
                    case 2:
                        // Deserialize as type 2
                        break;
                    case 3:
                        // Deserialize as type 3
                        break;
                }
                break;
            }
        }
    }
}

Option 1: Without using an abstract base class for your data classes.

The easiest I've found to do this is as follows:

  1. Declare your class using JToken as the field type for the unknown object.

     [JsonObject(MemberSerialization.OptIn)] public class ProtocolHeader { [JsonProperty("header")] private int _header; [JsonProperty("data")] private JToken _data; } 
  2. Expose the specialized data in properties.

     public ProtocolResult Result { get { if (_data == null || _header != ResultHeaderValue) return null; return _data.ToObject<ProtocolResult>(); } } public ProtocolError Error { get { if (_data == null || _header != ErrorHeaderValue) return null; return _data.ToObject<ProtocolError>(); } } 

Option 2: With using an abstract base class for your data classes.

Another option is to create an abstract base class for the various data types, and create a static method in the abstract base class to perform the type selection and proper deserialization. This is particularly useful when the type information is contained in the object itself (eg if header was a property inside the data object).

  1. The LoadBalancerConfiguration<T>._healthMonitor field has the type JObject , but the HealthMonitor property in the same class returns a HealthMonitor object.
  2. The HealthMonitor.FromJObject method performs the actual deserialization.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM