将嵌套集合转换为平面数据 Table
Converting nested collection into flat Data Table
我有一个嵌套的值集合(ID 和名称),存储为 XML
。我想在保持父子关系的同时将此 XML
转换为单个数据 Table。
<Apartment id="1A" name="Apartment 1A">
<ApartmentComponent id="300" name="Living Room" />
<ApartmentComponent id="301" name="Bathroom">
<ApartmentComponent id="2698" name="Tub" />
<ApartmentComponent id="8204" name="Sink" />
</ApartmentComponent>
</Apartment>
<Apartment id="2A" name="Apartment 2A">
<ApartmentComponent id="302" name="Dining Room">
<ApartmentComponent id="2635" name="Table" />
<ApartmentComponent id="2746" name="Cabinet" />
</ApartmentComponent>
<ApartmentComponent id="301" name="Bathroom">
<ApartmentComponent id="8204" name="Sink">
<ApartmentComponent id="56352" name="Drain Plug" />
</ApartmentComponent>
</ApartmentComponent>
</Apartment>
结果 table 看起来像这样...
+-------+--------------+-----------+--------------+
| ID | Value | Parent ID | Parent Value |
+-------+--------------+-----------+--------------+
| 1A | Apartment 1A | | |
+-------+--------------+-----------+--------------+
| 300 | Living Room | 1A | Apartment 1A |
+-------+--------------+-----------+--------------+
| 301 | Bathroom | 1A | Apartment 1A |
+-------+--------------+-----------+--------------+
| 2698 | Tub | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 8204 | Sink | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 2A | Apartment 2A | | |
+-------+--------------+-----------+--------------+
| 302 | Dining Room | 2A | Apartment 2A |
+-------+--------------+-----------+--------------+
| 2635 | Table | 302 | Dining Room |
+-------+--------------+-----------+--------------+
| 2746 | Cabinet | 302 | Dining Room |
+-------+--------------+-----------+--------------+
| 301 | Bathroom | 2A | Apartment 2A |
+-------+--------------+-----------+--------------+
| 8204 | Sink | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 56352 | Drain Plug | 8204 | Sink |
+-------+--------------+-----------+--------------+
我通常会先使用 XDocument
解析 XML。这会给我一个可以使用 LINQ
查询的集合。但是我不确定如何将生成的集合解析为平面 table 结构。我需要使用递归函数吗?
就像上面评论的 madreflection 一样,XContainer.Descendants()
将在不需要递归的情况下执行此操作 - 或者更确切地说,它会为您处理递归,一直到排水塞和更远的地方。
假设该代码段周围有一个 <root>
元素,因此 XDocument will parse it normally,
foreach (var element in xdoc.Root.Descendants())
{
(string, string, string, string) values = (
element.Attribute("id").Value,
element.Attribute("name").Value,
element.Parent?.Attribute("id")?.Value,
element.Parent?.Attribute("name")?.Value
);
Console.WriteLine(values);
}
您也可以选择此方法来获得所需的解决方案。
阶段 1:反序列化
Class 对象
注意:要同时了解 ChildItemCollection class,这是一篇很好的序列化 XML 且父子关系完整的文章。你可以在这里查看 C# Parent/child relationship and XML serialization
[XmlRoot("root")]
public class Apartment
{
public Apartment()
{
this.Children = new ChildItemCollection<Apartment, ApartmentComponent>(this);
}
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("ApartmentComponent")]
public ChildItemCollection<Apartment, ApartmentComponent> Children { get; private set; }
}
public class ApartmentComponent : IChildItem<Apartment>, IChildItem<ApartmentComponent>
{
public ApartmentComponent()
{
this.Children = new ChildItemCollection<ApartmentComponent, ApartmentComponent>(this);
}
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("ApartmentComponent")]
public ChildItemCollection<ApartmentComponent, ApartmentComponent> Children { get; private set; }
[XmlIgnore]
public Apartment ParentApartment { get; internal set; }
Apartment IChildItem<Apartment>.Parent
{
get
{
return this.ParentApartment;
}
set
{
this.ParentApartment = value;
}
}
[XmlIgnore]
public ApartmentComponent ParentComponent { get; internal set; }
ApartmentComponent IChildItem<ApartmentComponent>.Parent
{
get
{
return this.ParentComponent;
}
set
{
this.ParentComponent = value;
}
}
}
实施
XmlSerializer serializer = new XmlSerializer(typeof(List<Apartment>), new XmlRootAttribute("root"));
using (FileStream fileStream = new FileStream("XMLFile1.xml", FileMode.Open))
{
var result = (List<Apartment>)serializer.Deserialize(fileStream);
}
阶段 2:扁平化为平面数据。
它非常复杂,这是我可以采用的方法。但希望还有其他更好的选择。
Class 对象
public class ResultItem
{
public string Id { get; set; }
public string Value { get; set; }
public string ParentId { get; set; }
public string ParentValue { get; set; }
}
实施
XmlSerializer serializer = new XmlSerializer(typeof(List<Apartment>), new XmlRootAttribute("root"));
using (FileStream fileStream = new FileStream("XMLFile1.xml", FileMode.Open))
{
var result = (List<Apartment>)serializer.Deserialize(fileStream);
var items = result.Select(item => new ResultItem {
Id = item.Id,
Value = item.Name
}).ToList();
result.ForEach(apartment =>
{
apartment.Children.ToList().ForEach(component => {
setItemResult(component, items);
});
});
dataGridView1.DataSource = items;
}
private void setItemResult(ApartmentComponent apartmentComponent, List<ResultItem> items)
{
if (apartmentComponent.Children.Count > 0)
{
apartmentComponent.Children.ToList().ForEach(component => {
setItemResult(component, items);
});
}
var item = new ResultItem
{
Id = apartmentComponent.Id,
Value = apartmentComponent.Name
};
var parentApartment = apartmentComponent.ParentApartment;
var parentComponent = apartmentComponent.ParentComponent;
if (parentApartment != null)
{
item.ParentId = parentApartment.Id;
item.ParentValue = parentApartment.Name;
}
if (parentComponent != null)
{
item.ParentId = parentComponent.Id;
item.ParentValue = parentComponent.Name;
}
items.Add(item);
}
输出:
编码愉快,干杯!
我有一个嵌套的值集合(ID 和名称),存储为 XML
。我想在保持父子关系的同时将此 XML
转换为单个数据 Table。
<Apartment id="1A" name="Apartment 1A">
<ApartmentComponent id="300" name="Living Room" />
<ApartmentComponent id="301" name="Bathroom">
<ApartmentComponent id="2698" name="Tub" />
<ApartmentComponent id="8204" name="Sink" />
</ApartmentComponent>
</Apartment>
<Apartment id="2A" name="Apartment 2A">
<ApartmentComponent id="302" name="Dining Room">
<ApartmentComponent id="2635" name="Table" />
<ApartmentComponent id="2746" name="Cabinet" />
</ApartmentComponent>
<ApartmentComponent id="301" name="Bathroom">
<ApartmentComponent id="8204" name="Sink">
<ApartmentComponent id="56352" name="Drain Plug" />
</ApartmentComponent>
</ApartmentComponent>
</Apartment>
结果 table 看起来像这样...
+-------+--------------+-----------+--------------+
| ID | Value | Parent ID | Parent Value |
+-------+--------------+-----------+--------------+
| 1A | Apartment 1A | | |
+-------+--------------+-----------+--------------+
| 300 | Living Room | 1A | Apartment 1A |
+-------+--------------+-----------+--------------+
| 301 | Bathroom | 1A | Apartment 1A |
+-------+--------------+-----------+--------------+
| 2698 | Tub | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 8204 | Sink | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 2A | Apartment 2A | | |
+-------+--------------+-----------+--------------+
| 302 | Dining Room | 2A | Apartment 2A |
+-------+--------------+-----------+--------------+
| 2635 | Table | 302 | Dining Room |
+-------+--------------+-----------+--------------+
| 2746 | Cabinet | 302 | Dining Room |
+-------+--------------+-----------+--------------+
| 301 | Bathroom | 2A | Apartment 2A |
+-------+--------------+-----------+--------------+
| 8204 | Sink | 301 | Bathroom |
+-------+--------------+-----------+--------------+
| 56352 | Drain Plug | 8204 | Sink |
+-------+--------------+-----------+--------------+
我通常会先使用 XDocument
解析 XML。这会给我一个可以使用 LINQ
查询的集合。但是我不确定如何将生成的集合解析为平面 table 结构。我需要使用递归函数吗?
就像上面评论的 madreflection 一样,XContainer.Descendants()
将在不需要递归的情况下执行此操作 - 或者更确切地说,它会为您处理递归,一直到排水塞和更远的地方。
假设该代码段周围有一个 <root>
元素,因此 XDocument will parse it normally,
foreach (var element in xdoc.Root.Descendants())
{
(string, string, string, string) values = (
element.Attribute("id").Value,
element.Attribute("name").Value,
element.Parent?.Attribute("id")?.Value,
element.Parent?.Attribute("name")?.Value
);
Console.WriteLine(values);
}
您也可以选择此方法来获得所需的解决方案。
阶段 1:反序列化
Class 对象
注意:要同时了解 ChildItemCollection class,这是一篇很好的序列化 XML 且父子关系完整的文章。你可以在这里查看 C# Parent/child relationship and XML serialization
[XmlRoot("root")]
public class Apartment
{
public Apartment()
{
this.Children = new ChildItemCollection<Apartment, ApartmentComponent>(this);
}
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("ApartmentComponent")]
public ChildItemCollection<Apartment, ApartmentComponent> Children { get; private set; }
}
public class ApartmentComponent : IChildItem<Apartment>, IChildItem<ApartmentComponent>
{
public ApartmentComponent()
{
this.Children = new ChildItemCollection<ApartmentComponent, ApartmentComponent>(this);
}
[XmlAttribute("id")]
public string Id { get; set; }
[XmlAttribute("name")]
public string Name { get; set; }
[XmlElement("ApartmentComponent")]
public ChildItemCollection<ApartmentComponent, ApartmentComponent> Children { get; private set; }
[XmlIgnore]
public Apartment ParentApartment { get; internal set; }
Apartment IChildItem<Apartment>.Parent
{
get
{
return this.ParentApartment;
}
set
{
this.ParentApartment = value;
}
}
[XmlIgnore]
public ApartmentComponent ParentComponent { get; internal set; }
ApartmentComponent IChildItem<ApartmentComponent>.Parent
{
get
{
return this.ParentComponent;
}
set
{
this.ParentComponent = value;
}
}
}
实施
XmlSerializer serializer = new XmlSerializer(typeof(List<Apartment>), new XmlRootAttribute("root"));
using (FileStream fileStream = new FileStream("XMLFile1.xml", FileMode.Open))
{
var result = (List<Apartment>)serializer.Deserialize(fileStream);
}
阶段 2:扁平化为平面数据。
它非常复杂,这是我可以采用的方法。但希望还有其他更好的选择。
Class 对象
public class ResultItem
{
public string Id { get; set; }
public string Value { get; set; }
public string ParentId { get; set; }
public string ParentValue { get; set; }
}
实施
XmlSerializer serializer = new XmlSerializer(typeof(List<Apartment>), new XmlRootAttribute("root"));
using (FileStream fileStream = new FileStream("XMLFile1.xml", FileMode.Open))
{
var result = (List<Apartment>)serializer.Deserialize(fileStream);
var items = result.Select(item => new ResultItem {
Id = item.Id,
Value = item.Name
}).ToList();
result.ForEach(apartment =>
{
apartment.Children.ToList().ForEach(component => {
setItemResult(component, items);
});
});
dataGridView1.DataSource = items;
}
private void setItemResult(ApartmentComponent apartmentComponent, List<ResultItem> items)
{
if (apartmentComponent.Children.Count > 0)
{
apartmentComponent.Children.ToList().ForEach(component => {
setItemResult(component, items);
});
}
var item = new ResultItem
{
Id = apartmentComponent.Id,
Value = apartmentComponent.Name
};
var parentApartment = apartmentComponent.ParentApartment;
var parentComponent = apartmentComponent.ParentComponent;
if (parentApartment != null)
{
item.ParentId = parentApartment.Id;
item.ParentValue = parentApartment.Name;
}
if (parentComponent != null)
{
item.ParentId = parentComponent.Id;
item.ParentValue = parentComponent.Name;
}
items.Add(item);
}
输出:
编码愉快,干杯!