反序列化动态JSON文件C#NewtonSoft.JSON

时间:2022-04-15 05:15:59

Working on deserializing a dynamic JSON file which could contain 2 separate classes and I won't know which type of data will be in the array.

正在进行反序列化动态JSON文件,该文件可能包含2个单独的类,我不知道数组中将包含哪种类型的数据。

The problem is, I deserialize the root object to type "Base", "subtests" objects are deserialized to "Subtest", but the "subtests" array could be of type "Base" or of type "Subtest".

问题是,我将根对象反序列化为“Base”类型,“subtests”对象被反序列化为“Subtest”,但“subtests”数组可以是“Base”类型或“Subtest”类型。

QUESTION: How would I go about programatically determining that if the object contains "subtest", I deserialize to Base, and if it doesn't, it should deserialize to "Subtest"?

问题:我如何以编程方式确定如果对象包含“subtest”,我将反序列化为Base,如果不包含,则应将其反序列化为“Subtest”?

I really appreciate the help on this one since I'm on a short timeline.

因为我的时间很短,所以我非常感谢这方面的帮助。

(EDIT: Added comments to show what type each object should deserialize to) Here's an example (JSON DATA):

(编辑:添加注释以显示每个对象应反序列化的类型)以下是一个示例(JSON DATA):

{
// Deserializes to type "Base"
  "host": "123456",
  "last_time": "2014-09-15 07:04:49.205000",
  "name": "myName",
  "result": "FAIL",
  "serial": "12345",
  "start_time": "2014-09-15 06:53:36.976000",
// Deserializes to type "List<Subtest>"
  "subtests": [
    {
      "data": {
        "moredata": {
          "ver": "123",
          "real": 123
        }
      },
      "description": "Description of Data",
      "host": "123456",
      "last_time": "2014-09-15 20:32:31.095000",
      "name": "testname.py",
      "result": "PASS",
      "start_time": "2014-09-15 20:32:25.873000",
      "version": "2.014.09.15"
    },
    {
// Nested within Subtest Array, Should deserialize to type "Base" again
      "host": "123456",
      "last_time": "2014-09-15 07:04:49.205000",
      "name": "name of test suite",
      "result": "FAIL",
      "start_time": "2014-09-15 06:53:36.976000",
//Should deserialize to type "List<Subtest>"
      "subtests": [
        {
          "description": "Description of Data",
          "host": "123456",
          "last_time": "2014-09-15 06:53:40.440000",
          "name": "testname.py",
          "result": "FAIL",
          "start_time": "2014-09-15 06:53:36.976000",
          "version": "2.014.09.15"
        },
        {
          "description": "Test Description",
          "host": "123456",
          "last_time": "2014-09-15 06:54:34.905000",
          "name": "testname.py",
          "result": "PASS",
          "start_time": "2014-09-15 06:54:34.827000",
          "version": "2.014.09.15"
        },
        {
          "host": "123456",
          "last_time": "2014-09-15 06:55:01.156000",
          "name": "testname.py",
          "result": "FAIL",
          "start_time": "2014-09-15 06:55:01.156000",
          "version": "2.014.09.15"
        },

      ],
      "version": "1.45"
    }
  ],
  "version": "1.23"
}

Example (CODE):

示例(代码):

public class Base{
    public string host { get; set; }
    public DateTime last_time { get; set; }
    public string name { get; set; }
    public string result { get; set; }
    public string serial { get; set; }
    public DateTime start_time { get; set; }
    public List<Subtest> subtests { get; set; }
    public string version { get; set; }
}

public class Subtest {
    [JsonProperty("data")]
    public JObject Data { get; set; } // CHECK

    [JsonProperty("description")]
    public string Description { get; set; } // CHECK

    [JsonProperty("host")]
    public string Host { get; set; }

    [JsonProperty("info")]
    public List<StatusDetails> Info { get; set; }

    [JsonProperty("last_time")]
    public DateTime LastRunTime { get; set; }

    [JsonProperty("name")]
    public string TestName { get; set; }

    [JsonProperty("result")]
    public string SubtestRunResult { get; set; }

    [JsonProperty("start_time")]
    public DateTime StartTime { get; set; }

    [JsonProperty("trace")]
    public List<object> Trace { get; set; }

    [JsonProperty("version")]
    public string Version { get; set; }
}

2 个解决方案

#1


3  

I would rework your classes to form a hierarchy. I'm probably missing a properties here, but you get the picture. The important bit is the converter.

我会修改你的类来形成一个层次结构。我可能在这里错过了一处房产,但是你得到的照片。重要的是转换器。

public abstract class TestBase
{
    public string Host { get; set; }

    [JsonProperty("last_time")]
    public DateTime LastTime { get; set; }

    public string Name { get; set; }
    public string Result { get; set; }

    [JsonProperty("start_time")]
    public DateTime StartTime { get; set; }
    public string Version { get; set; }
}

public class TestSuite : TestBase
{
    public string Serial { get; set; }
    public List<TestBase> Subtests { get; set; }
}

public class Subtest : TestBase
{
    public JObject Data { get; set; }

    public string Description { get; set; }
}

Then, you need a custom converter to choose the correct type based on the existence of the subtests property:

然后,您需要一个自定义转换器,以根据子测试属性的存在选择正确的类型:

public class TestBaseConverter : JsonConverter
{
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        JObject obj = serializer.Deserialize<JObject>(reader);

        TestBase result = null;

        if (obj["subtests"] != null)
        {
            result = new TestSuite();
        }
        else 
        {
            result = new Subtest();
        }

        serializer.Populate(obj.CreateReader(), result);

        return result;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(TestBase).IsAssignableFrom(objectType);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(
        JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

You would then use it like this:

然后你会像这样使用它:

TestSuite suite = JsonConvert.DeserializeObject<TestSuite>(
    json, new TestBaseConverter());

#2


1  

What I ended up doing was adding a List<Subtest> property to my Subtest class and checked for a null value in a recursive foreach loop function. Not as pretty as I'd like it to be, but better than parsing it down and deserializing each subtest object individually.

我最终做的是将一个List 属性添加到我的Subtest类中,并在递归的foreach循环函数中检查空值。不像我希望的那样漂亮,但比解析它并单独反序列化每个子测试对象更好。

private static void GetSubtest(List<Subtest> subtestList) {
        foreach (var subtest in subtestList) {
            if (subtest.Subtests != null) {
                GetSubtest(subtest.Subtests);
            }
            else {
                // add data to Vertica cluster
            }
        }
    }

Long day, really appreciate all you guys trying to help. New to JSON so I just couldn't get my head around it. Hopefully this helps someone else out in the future. Just throw a comment on here if you need more of an explanation.

漫长的一天,真的很感谢所有你们想要帮助的人。 JSON的新手,所以我无法理解它。希望这有助于将来的其他人。如果您需要更多解释,请在此处发表评论。

#1


3  

I would rework your classes to form a hierarchy. I'm probably missing a properties here, but you get the picture. The important bit is the converter.

我会修改你的类来形成一个层次结构。我可能在这里错过了一处房产,但是你得到的照片。重要的是转换器。

public abstract class TestBase
{
    public string Host { get; set; }

    [JsonProperty("last_time")]
    public DateTime LastTime { get; set; }

    public string Name { get; set; }
    public string Result { get; set; }

    [JsonProperty("start_time")]
    public DateTime StartTime { get; set; }
    public string Version { get; set; }
}

public class TestSuite : TestBase
{
    public string Serial { get; set; }
    public List<TestBase> Subtests { get; set; }
}

public class Subtest : TestBase
{
    public JObject Data { get; set; }

    public string Description { get; set; }
}

Then, you need a custom converter to choose the correct type based on the existence of the subtests property:

然后,您需要一个自定义转换器,以根据子测试属性的存在选择正确的类型:

public class TestBaseConverter : JsonConverter
{
    public override object ReadJson(
        JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        JObject obj = serializer.Deserialize<JObject>(reader);

        TestBase result = null;

        if (obj["subtests"] != null)
        {
            result = new TestSuite();
        }
        else 
        {
            result = new Subtest();
        }

        serializer.Populate(obj.CreateReader(), result);

        return result;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(TestBase).IsAssignableFrom(objectType);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(
        JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotSupportedException();
    }
}

You would then use it like this:

然后你会像这样使用它:

TestSuite suite = JsonConvert.DeserializeObject<TestSuite>(
    json, new TestBaseConverter());

#2


1  

What I ended up doing was adding a List<Subtest> property to my Subtest class and checked for a null value in a recursive foreach loop function. Not as pretty as I'd like it to be, but better than parsing it down and deserializing each subtest object individually.

我最终做的是将一个List 属性添加到我的Subtest类中,并在递归的foreach循环函数中检查空值。不像我希望的那样漂亮,但比解析它并单独反序列化每个子测试对象更好。

private static void GetSubtest(List<Subtest> subtestList) {
        foreach (var subtest in subtestList) {
            if (subtest.Subtests != null) {
                GetSubtest(subtest.Subtests);
            }
            else {
                // add data to Vertica cluster
            }
        }
    }

Long day, really appreciate all you guys trying to help. New to JSON so I just couldn't get my head around it. Hopefully this helps someone else out in the future. Just throw a comment on here if you need more of an explanation.

漫长的一天,真的很感谢所有你们想要帮助的人。 JSON的新手,所以我无法理解它。希望这有助于将来的其他人。如果您需要更多解释,请在此处发表评论。