Convert nested JSON to CSV

前端 未结 2 1764
温柔的废话
温柔的废话 2021-02-15 02:54

I am converting a nested JSON object with more than 10 levels to CSV file in C# .NET.

I have been using JavaScriptSerializer().Deserialize(json)

2条回答
  •  鱼传尺愫
    2021-02-15 03:40

    There's an inconsistency in your request: you want a row to be generated for the root object, which has children, but you don't want a row to be generated for the "F3[2]" object, which also has children. So it sounds like your rule is, "print a row for an object with at least one primitive-value property, as long as that object is either the root object or does not have descendant objects with at a least one primitive-value property". That's a little tricky, but can be done with LINQ to JSON

            var obj = JObject.Parse(json);
    
            // Collect column titles: all property names whose values are of type JValue, distinct, in order of encountering them.
            var values = obj.DescendantsAndSelf()
                .OfType()
                .Where(p => p.Value is JValue)
                .GroupBy(p => p.Name)
                .ToList();
    
            var columns = values.Select(g => g.Key).ToArray();
    
            // Filter JObjects that have child objects that have values.
            var parentsWithChildren = values.SelectMany(g => g).SelectMany(v => v.AncestorsAndSelf().OfType().Skip(1)).ToHashSet();
    
            // Collect all data rows: for every object, go through the column titles and get the value of that property in the closest ancestor or self that has a value of that name.
            var rows = obj
                .DescendantsAndSelf()
                .OfType()
                .Where(o => o.PropertyValues().OfType().Any())
                .Where(o => o == obj || !parentsWithChildren.Contains(o)) // Show a row for the root object + objects that have no children.
                .Select(o => columns.Select(c => o.AncestorsAndSelf()
                    .OfType()
                    .Select(parent => parent[c])
                    .Where(v => v is JValue)
                    .Select(v => (string)v)
                    .FirstOrDefault())
                    .Reverse() // Trim trailing nulls
                    .SkipWhile(s => s == null)
                    .Reverse());
    
            // Convert to CSV
            var csvRows = new[] { columns }.Concat(rows).Select(r => string.Join(",", r));
            var csv = string.Join("\n", csvRows);
    
            Console.WriteLine(csv);
    

    Using

    public static class EnumerableExtensions
    {
        // http://stackoverflow.com/questions/3471899/how-to-convert-linq-results-to-hashset-or-hashedset
        public static HashSet ToHashSet(this IEnumerable source)
        {
            return new HashSet(source);
        }
    }
    

    Which outputs:

    F1,F2,E1,E2,D1,D2
    1,2
    1,2,3,4
    1,2,5,6
    1,2,7,8,9,10
    

提交回复
热议问题