【问题标题】:How do I parse a JSON object in C# when I don't know key and properties don't have names?当我不知道键和属性没有名称时,如何在 C# 中解析 JSON 对象?
【发布时间】:2015-06-23 06:09:18
【问题描述】:

这是我未解析的 JSON:

{ "1": [ [ [ 2015, 6, 1, 8, 0, 0, 3600, "Europe/London", "BST", 1 ], [ 2015, 6, 1, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 1, 8, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 1, 10, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 1, 9, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 1, 10, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 1, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 1, 11, 0, 0, 3600, “欧洲/伦敦”、“英国夏令时”、1 ] ] ]、“2”:[ [ [ 2015, 6, 2, 8, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 2, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 2, 8, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 2, 10, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 2, 9, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 2, 10, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 2, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 2, 11, 0, 0, 3600, “欧洲/伦敦”、“英国夏令时”、1 ] ] ]、“3”:[ [ [ 2015, 6, 3, 8, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 3, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 3, 8, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 3, 10, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 3, 9, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 3, 10, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 3, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 3, 11, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ] ] ], "4": [ [ [ 2015, 6, 4, 8, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 4, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 4, 8, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 4, 10, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 4, 9, 0, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 4, 10, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ] ], [ [ 2015, 6, 4, 9, 30, 0, 3600, "欧洲/伦敦", "BST", 1 ], [ 2015, 6, 4, 11, 0, 0, 3600, “欧洲/伦敦”、“英国夏令时”、1 ] ] ]}

这是基本格式:

根

 Day of the month

      time slot

           time slot start

           time slot end

这是格式化后的样子:

我终其一生都无法弄清楚如何解决这个问题。这个问题应该类似于How do I parse a JSON object in C# when I don't know the key in advance?

如果我执行以下操作,我会收到“System.InvalidOperationException:无法访问 Newtonsoft.Json.Linq.JValue 上的子值。”

        JObject objTimes = JObject.Parse(strJson);

        foreach (var day in objTimes["1"])
        {
            divTimes.InnerHtml += day[0][0][0];
        }

【问题讨论】:

  • 用 JSON 的全部内容蒙蔽我们是没有帮助的。您可以将其减少到 5 行,并且仍然代表手头 JSON 的性质。
  • 如果这个问题与另一个问题相似,那么另一个问题有帮助吗?如果不是为什么?您可能会收到类似的答案。请阅读How to Ask
  • 尝试将您的 JSON 发布到 jsonformatter.curiousconcept.com,您将能够更轻松地查看结构。根对象是某种字典,键是整数值字符串,值是嵌套数组。使用JObject.Parse 加载它。
  • 看起来最里面的数组代表了某个类的字段:[ 2015, 6, 3, 9, 30, 0, 3600, "Europe/London", "BST", 1 ] 你能设计一个代表这个数据的类吗?我猜不出每个字段的含义。
  • 我减少了 JSON。另一个问题和答案没有帮助,因为我的 JSON 格式很奇怪,但我无法控制 JSON。我认为另一个问题没有帮助,因为我的 JSON 不是“名称:值”格式。

标签: c# json json.net


【解决方案1】:

你可以做的是设计一个类(命名,例如,Entry)来表示代表 JSON 中最低级别数组的数据:[ 2015, 6, 3, 9, 30, 0, 3600, "Europe/London", "BST", 1 ]。然后创建一个JsonConverter 将数组值顺序加载到数组中。最后酌情反序列化外部字典和数组:

这是一个可能的类来表示单个内部数组的数据:

[JsonConverter(typeof(EntryConverter))]
public class Entry
{
    public Entry()
    {
        // You need to determine whether the time numbers in the JSON are in UTC or in the timezone given in the "TimeZone" field.  If
        // Local, change to DateTimeKind.Local
        DateTime = new DateTime(0, DateTimeKind.Utc); 
    }

    public DateTime DateTime { get; set; }
    public int Number { get; set; } // That 3600 thing.  No idea what it's for.
    public string Destination { get; set; }
    public string TimeZone { get; set; }
    // Not all time zone offsets are integers: https://en.wikipedia.org/wiki/UTC%E2%88%9204:30
    public decimal GmtOffset { get; set; }
}

接下来,转换器:

public class EntryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Entry).IsAssignableFrom(objectType);
    }

    void SetField(Entry entry, int index, JValue value)
    {
        switch (index)
        {
            case 0:
                entry.DateTime = new DateTime((int)value, entry.DateTime.Month, entry.DateTime.Day, entry.DateTime.Hour, entry.DateTime.Minute, entry.DateTime.Second, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 1:
                entry.DateTime = new DateTime(entry.DateTime.Year, (int)value, entry.DateTime.Day, entry.DateTime.Hour, entry.DateTime.Minute, entry.DateTime.Second, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 2:
                entry.DateTime = new DateTime(entry.DateTime.Year, entry.DateTime.Month, (int)value, entry.DateTime.Hour, entry.DateTime.Minute, entry.DateTime.Second, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 3:
                entry.DateTime = new DateTime(entry.DateTime.Year, entry.DateTime.Month, entry.DateTime.Day, (int)value, entry.DateTime.Minute, entry.DateTime.Second, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 4:
                entry.DateTime = new DateTime(entry.DateTime.Year, entry.DateTime.Month, entry.DateTime.Day, entry.DateTime.Hour, (int)value, entry.DateTime.Second, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 5:
                entry.DateTime = new DateTime(entry.DateTime.Year, entry.DateTime.Month, entry.DateTime.Day, entry.DateTime.Hour, entry.DateTime.Minute, (int)value, entry.DateTime.Millisecond, entry.DateTime.Kind);
                break;
            case 6:
                entry.Number = (int)value;
                break;
            case 7:
                entry.Destination = (string)value;
                break;
            case 8:
                entry.TimeZone = (string)value;
                break; 
            case 9:
                entry.GmtOffset = (decimal)value;
                break;
            default:
                throw new IndexOutOfRangeException(index.ToString());
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var array = JArray.Load(reader);
        if (array == null)
            return existingValue;
        var entry = (existingValue as Entry ?? new Entry());
        for (int i = 0; i < array.Count; i++)
        {
            SetField(entry, i, (JValue)array[i]);
        }
        return entry;
    }

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

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

然后像这样使用它:

        var root = JsonConvert.DeserializeObject<Dictionary<int, List<List<Entry>>>>(json);
        Debug.WriteLine(JsonConvert.SerializeObject(root, Formatting.Indented));

在这里,为了简洁起见,我将所有时间信息压缩为一个 DateTime,尽管您可能不想这样做。此外,尚不清楚时间信息是 UTC 还是当地时间。如果是本地的,那么我对DateTime.Kind 的使用是错误的。

如果需要,您可以扩展转换器以按相同顺序序列化字段。

【讨论】:

  • 谢谢,我试试看。
【解决方案2】:

这里用 JSON.NET 实现有点松散。

首先定义类。 TimeSlotInfo 保存有关 DayOfMonth 和该月中可用的时间段的信息:

public class TimeSlotInfo
{
    public int DayOfMonth { get; set; }

    public List<TimeSlotRange> TimeSlots { get; set; }

    public TimeSlotInfo()
    {
        TimeSlots = new List<TimeSlotRange>();
    }
}

public class TimeSlotRange
{
    public TimeSlotItem Start { get; set; }

    public TimeSlotItem End { get; set; }
}

public class TimeSlotItem
{
    public int Year { get; set; }

    public int Month { get; set; }

    public int Day { get; set; }

    public int Hour { get; set; }

    public int Minute { get; set; }

    public int Second { get; set; }

    public int SecondsInHour { get; set; }

    public string TimezoneStr { get; set; }

    public string BST { get; set; }

    public int SomeNum { get; set; }

    public DateTime AsDateTime
    {
        get { return new DateTime(Year, Month, Day, Hour, Minute, Second); }
    }

    public static TimeSlotItem CreateFromJTokenList(List<JToken> tokenList)
    {
        try
        {
            var result = new TimeSlotItem
            {
                Year = tokenList[0].Value<int>(),
                Month = tokenList[1].Value<int>(),
                Day = tokenList[2].Value<int>(),
                Hour = tokenList[3].Value<int>(),
                Minute = tokenList[4].Value<int>(),
                Second = tokenList[5].Value<int>(),
                SecondsInHour = tokenList[6].Value<int>(),
                TimezoneStr = tokenList[7].Value<string>(),
                BST = tokenList[8].Value<string>(),
                SomeNum = tokenList[9].Value<int>()
            };

            return result;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

然后你需要一个解析器来填充这些对象:

var timeSlotInfoList = new List<TimeSlotInfo>();

var jsonVal = System.IO.File.ReadAllText(@"C:\test.json"); // Somehow get JSON string into jsonVal. Not neccessarily from file.
var obj = JsonConvert.DeserializeObject(jsonVal) as JObject;
if (obj != null)
{
    var jobj = obj.AsQueryable();
    foreach (JProperty item in jobj)
    {
        try
        {
            var dayOfMonth = Convert.ToInt32(item.Name);

            var timeSlotInfo = new TimeSlotInfo
            {
                DayOfMonth = dayOfMonth
            };
            timeSlotInfoList.Add(timeSlotInfo);

            var timeSlots = item.Value.ToList();
            foreach (var timeSlotPair in timeSlots)
            {
                var timeSlotPairList = timeSlotPair.ToList();
                if (timeSlotPairList.Count != 2)
                    continue;

                var slotItemStart = TimeSlotItem.CreateFromJTokenList(timeSlotPairList[0].ToList());
                var slotItemEnd = TimeSlotItem.CreateFromJTokenList(timeSlotPairList[0].ToList());

                if (slotItemStart != null && slotItemEnd != null)
                {
                    timeSlotInfo.TimeSlots.Add(new TimeSlotRange
                    {
                        Start = slotItemStart,
                        End = slotItemEnd,
                    });
                }
            }

        }
        catch (Exception)
        {
        }
    }
}

最后timeSlotInfoList 包含月份的所有日期及其时间段的列表(范围从开始到结束)。

此代码可能需要对异常、验证等进行更多调整,但这是解析此类事物的一些基本概念。 如果我犯了任何错误,我会很感激一些评论。无论如何,希望它有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-11
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多