当我不知道密钥和属性没有名称时,如何在C#中解析JSON对象?

时间:2015-06-23 06:09:18

标签: c# json json.net

这是我未解析的JSON:

  

{“1”:[[[2015,6,1,8,0,0,3600,“欧洲/伦敦”,“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,   “欧洲/伦敦”,“BST”,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,   “欧洲/伦敦”,“BST”,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,   “欧洲/伦敦”,“BST”,1]]]}

这是基本格式:

ROOT

 Day of the month

      time slot

           time slot start

           time slot end

这就是它的格式:

JSON Viewer

我无法弄清楚我的生活如何钻研这个。此问题应与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];
        }

2 个答案:

答案 0 :(得分:0)

您可以做的是设计一个类(命名为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的使用是错误的。

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

答案 1 :(得分:0)

这是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包含所有星期几及其时间段的列表(范围从开始到结束)。

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