第三方API嵌套JSON结构变体适配方案咨询:Property3为对象/数组时优雅获取目标值
Great question! Dealing with API responses that flip between object and array types for the same field is a super common headache, and you don’t need to rely on two separate models plus error handling to solve it. Here are a few elegant, no-preprocessing solutions:
Option 1: Use JToken for Dynamic Traversal (Most Flexible)
Instead of sticking to a strict strongly-typed model, use Json.NET's JObject and JToken to dynamically navigate the JSON structure. This lets you check whether Property3 is an object or array on the fly:
using Newtonsoft.Json.Linq; // Parse the raw JSON string var jObject = JObject.Parse(MyJsonString); // Locate Property3 and handle both object/array cases var property3Token = jObject["Property1"]["Property2B"]["Property3"]; JToken targetProperty3 = property3Token.Type == JTokenType.Array ? property3Token.First() // Take the first element if it's an array : property3Token; // Use directly if it's an object // Extract the desired value string result = targetProperty3["Property4"]["Property5"]["Property6"][1]["Property7D"]["X"].Value<string>();
You can even condense this with SelectToken for cleaner code:
var result = jObject.SelectToken("$.Property1.Property2B.Property3") .SelectToken(token => token.Type == JTokenType.Array ? token[0] : token) .SelectToken("Property4.Property5.Property6[1].Property7D.X") .Value<string>();
Option 2: Update Your Strongly-Typed Model with a Custom Converter
If you want to keep using your existing model structure, create a custom JsonConverter that handles both object and array cases for Property3. This way, deserialization will automatically normalize the structure for you:
Step 1: Update Your Model
Add the converter attribute to the Property3 field in your Property2B class:
public class Property2B { [JsonConverter(typeof(SingleItemOrArrayConverter<Property3>))] public Property3 Property3 { get; set; } } // Keep your existing Rootobject, Property1, Property3, Property4, etc. classes unchanged
Step 2: Implement the Custom Converter
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; public class SingleItemOrArrayConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(T); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var token = JToken.Load(reader); // If it's an array, deserialize the first element; otherwise deserialize the object directly return token.Type == JTokenType.Array ? token.First().ToObject<T>(serializer) : token.ToObject<T>(serializer); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // Optional: If you need to serialize back to JSON, adjust this to match your API's expected format serializer.Serialize(writer, value); } }
Now your original deserialization code works for both JSON variants:
Rootobject obj = JsonConvert.DeserializeObject<Rootobject>(MyJsonString); string result = obj.Property1.Property2B.Property3.Property4.Property5.Property6[1].Property7D.X;
Option 3: Use dynamic (Quick but Unsafe)
For a quick-and-dirty solution (not recommended for production code due to lack of type safety), you can use dynamic to bypass strict typing:
dynamic obj = JsonConvert.DeserializeObject(MyJsonString); dynamic property3 = obj.Property1.Property2B.Property3; // Check if Property3 is an array and grab the first element if needed var targetProperty3 = property3 is System.Collections.IEnumerable ? property3[0] : property3; string result = targetProperty3.Property4.Property5.Property6[1].Property7D.X;
Recommendation
If you prefer flexibility and don’t mind moving away from strict models, go with Option 1. If you want to maintain type safety and keep using your existing model structure, Option 2 is the most robust choice. Both avoid JSON preprocessing and messy error-handling switches between models.
内容的提问来源于stack exchange,提问作者Michael Plevan

