C#中从嵌套复杂JSON提取指定字段的问题及模型优化需求
Solution to Extract Test Data from Nested JSON
First, let's fix the core issues with your current model and implement a robust way to parse the nested structure and extract the data you need.
Why Your Current Model Isn't Working
Your existing classes are too simplistic and miss key parts of the JSON structure:
- The
TestSuiteclass is empty, so it can't deserialize any nested test-suite or test-fixture elements. - The JSON uses
@prefixed properties (like@methodname) which require explicit mapping with[JsonProperty]attributes. - Nested
test-suiteelements can be either single objects or arrays—your model doesn't handle this variability. - You haven't defined classes for
TestFixtureorTestCase, which are where themethodnameandfullnamevalues live.
Step 1: Define Proper C# Models
We'll use Newtonsoft.Json (Json.NET) for deserialization since it handles complex nested structures and single/array variability well. Here are the complete model classes:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; // Converter to handle properties that can be single objects or arrays public class SingleOrArrayConverter<T> : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(List<T>); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var token = JToken.Load(reader); return token.Type == JTokenType.Array ? token.ToObject<List<T>>() : new List<T> { token.ToObject<T>() }; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException("Writing is not supported for this converter"); } } // Base class for shared properties between TestSuite and TestFixture public class TestContainer { [JsonProperty("@id")] public string Id { get; set; } [JsonProperty("@name")] public string Name { get; set; } [JsonProperty("@fullname")] public string FullName { get; set; } [JsonProperty("@runstate")] public string RunState { get; set; } [JsonProperty("@testcasecount")] public string TestCaseCount { get; set; } [JsonProperty("properties")] public PropertiesContainer Properties { get; set; } } public class TestSuite : TestContainer { [JsonProperty("test-suite")] [JsonConverter(typeof(SingleOrArrayConverter<TestContainer>))] public List<TestContainer> ChildTestContainers { get; set; } } public class TestFixture : TestContainer { [JsonProperty("@classname")] public string ClassName { get; set; } [JsonProperty("test-case")] [JsonConverter(typeof(SingleOrArrayConverter<TestCase>))] public List<TestCase> TestCases { get; set; } } public class PropertiesContainer { [JsonProperty("property")] [JsonConverter(typeof(SingleOrArrayConverter<Property>))] public List<Property> Properties { get; set; } } public class Property { [JsonProperty("@name")] public string Name { get; set; } [JsonProperty("@value")] public string Value { get; set; } } public class TestCase { [JsonProperty("@id")] public string Id { get; set; } [JsonProperty("@name")] public string Name { get; set; } [JsonProperty("@fullname")] public string FullName { get; set; } [JsonProperty("@methodname")] public string MethodName { get; set; } [JsonProperty("@classname")] public string ClassName { get; set; } [JsonProperty("@runstate")] public string RunState { get; set; } [JsonProperty("@seed")] public string Seed { get; set; } [JsonProperty("properties")] public PropertiesContainer Properties { get; set; } } // Root class to match the top-level JSON structure public class TestRoot { [JsonProperty("test-suite")] [JsonConverter(typeof(SingleOrArrayConverter<TestContainer>))] public List<TestContainer> TestContainers { get; set; } } // Class to hold the extracted data you need public class ExtractedTestData { public string MethodName { get; set; } public string FullName { get; set; } public List<Property> Properties { get; set; } = new List<Property>(); }
Step 2: Recursively Extract the Data
We'll write a helper class to traverse the nested structure and collect all the methodname, fullname, and property values:
public class TestDataExtractor { public List<ExtractedTestData> ExtractAllTestData(string jsonContent) { var root = JsonConvert.DeserializeObject<TestRoot>(jsonContent); var extractedData = new List<ExtractedTestData>(); TraverseContainers(root.TestContainers, extractedData); return extractedData; } private void TraverseContainers(List<TestContainer> containers, List<ExtractedTestData> extractedData) { if (containers == null) return; foreach (var container in containers) { if (container is TestSuite suite) { // Recurse into child test suites/fixtures TraverseContainers(suite.ChildTestContainers, extractedData); } else if (container is TestFixture fixture) { // Extract data from each test case in the fixture foreach (var testCase in fixture.TestCases ?? new List<TestCase>()) { var data = new ExtractedTestData { MethodName = testCase.MethodName, FullName = testCase.FullName }; // Add properties from the fixture if (fixture.Properties?.Properties != null) { data.Properties.AddRange(fixture.Properties.Properties); } // Add properties from the test case itself if (testCase.Properties?.Properties != null) { data.Properties.AddRange(testCase.Properties.Properties); } extractedData.Add(data); } } } } }
Step 3: Use the Extractor
Here's how to use this code to read your JSON file and get the data you need:
using System.IO; // Read your JSON file string json = File.ReadAllText("path/to/your/test-file.json"); // Extract the data var extractor = new TestDataExtractor(); List<ExtractedTestData> result = extractor.ExtractAllTestData(json); // Example: Iterate over the results foreach (var item in result) { Console.WriteLine($"Method Name: {item.MethodName}"); Console.WriteLine($"Full Name: {item.FullName}"); Console.WriteLine("Properties:"); foreach (var prop in item.Properties) { Console.WriteLine($"- {prop.Name}: {prop.Value}"); } Console.WriteLine("---"); }
Key Notes
- The
SingleOrArrayConverterensures that properties liketest-suiteandpropertyare deserialized correctly whether they're single objects or arrays. - We use inheritance (
TestContaineras base class) to avoid duplicating shared properties betweenTestSuiteandTestFixture. - The recursive traversal ensures we don't miss any nested test cases, no matter how deep they are in the JSON structure.
内容的提问来源于stack exchange,提问作者Nightwolf
相关产品推荐
相关产品推荐

