【发布时间】:2021-02-10 07:43:51
【问题描述】:
我正在尝试在 .NET 中构建数据管道。我得到了一个xsd 并使用了XML Schema Definition Tool 来生成代表对象模型的C# 类。为了将此数据加载到我的数据存储中,我需要将来自 XML 的数据转换为与我的应用程序架构相匹配的结构并收集/重复数据删除元素。为此,我有一个解析器类,它将读取文件并将内容加载到本地集合中,然后将其加载到我的数据库中。我看到了两种选择 -
- 使用
XmlReader手动循环XML 并提取我需要的数据,将其加载到流中的本地集合中。这是不可取的,因为它没有利用给我的强类型/严格的xsd,并且需要很多硬编码的东西,比如while (reader.Read()),检查特定的XML节点,然后`reader.GetAttribute("硬编码字符串")。 - 使用
XmlSerializer一次反序列化整个文件,然后循环遍历反序列化的集合并插入到我的本地集合中。这是不可取的,因为文件可能非常大,而且这种方法迫使我循环遍历所有数据两次(一次反序列化,一次将数据提取到我的本地集合)。
理想情况下,我希望通过某种方式注册要执行的委托,因为每个对象都被反序列化以插入到我的本地集合中。框架中有什么东西可以让我这样做吗?要求如下:
- 高性能 - 只循环一次数据。
- 功能性 - 在反序列化期间将数据插入到本地集合中。
- 可维护 - 利用通过
xsd生成的强类型类。
我创建了一个最小的例子来说明我的观点。
示例 XML 文件:
<Hierarchy xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.example.com/example">
<Children>
<Child ChildId="1" ChildName="First">
<Parents>
<Parent ParentId="1" ParentName="First" RelationshipStart="1900-01-01T00:00:00"/>
<Parent ParentId="2" ParentName="Second" RelationshipStart="2000-01-01T00:00:00"/>
</Parents>
</Child>
<Child ChildId="2" ChildName="Second">
<Parents>
<Parent ParentId="2" ParentName="Second" RelationshipStart="1900-01-01T00:00:00"/>
<Parent ParentId="3" ParentName="Third" RelationshipStart="2000-01-01T00:00:00"/>
</Parents>
</Child>
</Children>
</Hierarchy>
我正在尝试加载的本地集合:
public Dictionary<int, string> Parents { get; }
public Dictionary<int, string> Children { get; }
public List<Relationship> Relationships { get; }
手动版本(不可维护且不使用xsd):
public void ParseFileManually(string fileName)
{
using (var reader = XmlReader.Create(fileName))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Hierarchy")
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Child")
{
int childId = int.Parse(reader.GetAttribute("ChildId"));
string childName = reader.GetAttribute("ChildName");
Children[childId] = childName;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Parent")
{
int parentId = int.Parse(reader.GetAttribute("ParentId"));
string parentName = reader.GetAttribute("ParentName");
DateTime relationshipStart = DateTime.Parse(reader.GetAttribute("RelationshipStart"));
Parents[parentId] = parentName;
Relationships.Add(
new Relationship{
ParentId = parentId,
ChildId = childId,
Start = relationshipStart
});
}
else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Child")
{
break;
}
}
}
}
}
}
}
}
反序列化版本(循环数据两次):
public void ParseFileWithDeserialize(string fileName)
{
var serializer = new XmlSerializer(typeof(Hierarchy));
using (var fileStream = new FileStream(fileName, FileMode.Open))
{
var fileData = (Hierarchy) serializer.Deserialize(fileStream);
foreach (var child in fileData.Children)
{
Children[child.ChildId] = child.ChildName;
foreach (var parent in child.Parents)
{
Parents[parent.ParentId] = parent.ParentName;
Relationships.Add(
new Relationship
{
ParentId = parent.ParentId,
ChildId = child.ChildId,
Start = parent.RelationshipStart
});
}
}
}
}
【问题讨论】:
-
这看起来像是stackoverflow.com/questions/27365029/…的复制品
-
@StuartSmith 为什么你认为这是重复的?由于 OP 代码中的错误,链接的问题似乎是反序列化代码根本不起作用的问题。在我的问题中,我提供的代码工作正常,而是我试图了解是否有一种有效的方法来完成我想要做的事情。
-
我认为您采取了错误的方法。首先,您没有架构,因此要使用 xsd.exe,您需要创建架构。其次,您真正的目标是加载数据库,因此第一步是设计数据库中的表。设计表格后,您应该根据数据库的结构解析 xml。数据库中的表是二维的,xml 文件有两层以上。设计数据库时有两个目标 1) 速度 2) 大小。两者之间存在权衡。
-
你总是可以创建一个只有一张表的数据库,但你最终会复制数据。例如,如果您的父母有 4 个孩子。一列是父名称,它将为 4 个孩子中的每一个重复。因此,如果您是父表和子表,然后父名称只输入一次。但是,您需要加入父数据和子数据以获得关系,这需要更多时间来精确数据。
-
给出数据库结构。分两步做额外的工作。如果使用序列化,则首先创建以 xml 结构组织的 c# 类,然后必须转换为数据库结构。一步完成,解析xml,看起来像数据库结构,效率更高。
标签: c# .net xml xsd xml-serialization