Convert nested JSON to CSV

前端 未结 2 1765
温柔的废话
温柔的废话 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:37

    I wrote this and it is working for me Here we save all breadcrumps of object tree in headers with format prop_prop And save jarray property objects in headers in format prop1

        public Dictionary<string, string> ComplexJsonToDictionary(JObject jObject, Dictionary<string, string> result, string field)
        {
            foreach (var property in jObject.Properties())
            {
                var endField = field + (string.IsNullOrEmpty(field) ? "" : "_") + property.Name;
    
                var innerDictionary = new Dictionary<string, string>();
                try
                {
                    var innerValue = JObject.Parse(Convert.ToString(property.Value));
    
    
                    result.AddOrOverride(ComplexJsonToDictionary(innerValue, innerDictionary, endField));
                }
                catch (Exception)
                {
                    try
                    {
                        var innerValues = JArray.Parse(Convert.ToString(property.Value));
                        try
                        {
                            var i = 0;
                            foreach (var token in innerValues)
                            {
                                var innerValue = JObject.Parse(Convert.ToString(token));
    
                                result.AddOrOverride(ComplexJsonToDictionary(innerValue, innerDictionary, endField+i++));
                            }
                        }
                        catch (Exception)
                        {
                            result.Add(endField, string.Join(",", innerValues.Values<string>()));
                        }
                    }
                    catch (Exception)
                    {
                        result.Add(endField, property.Value.ToString());
                    }
                }
            }
            return result;
        }
    

    Thanks for atantion and please write review if appropriate.

    0 讨论(0)
  • 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<JProperty>()
                .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<JObject>().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<JObject>()
                .Where(o => o.PropertyValues().OfType<JValue>().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<JObject>()
                    .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<T> ToHashSet<T>(this IEnumerable<T> source)
        {
            return new HashSet<T>(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
    
    0 讨论(0)
提交回复
热议问题