XmlReader 连续读取
XmlReader read continually
我有一个非常大的 xml 文件。这是 xml 格式的简化版本。
<?xml version='1.0' encoding='UTF-8'?>
<Sender>
<SenderID>571099948</SenderID>
<Sponsors>
<Sponsor>
<SponsorID>TEST01</SponsorID>
<Contracts>
<Contract>
<ContractID>000001</ContractID>
<Member>
<SSN>1111111111</SSN>
<Gender>M</Gender>
<Benefits>
<Benefit BenefitType="AAA">
</Benefit>
<Benefit BenefitType="BBB">
</Benefit>
</Benefits>
</Member>
<Member>
<SSN>4444444444</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="AAA">
</Benefit>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000002</ContractID>
<Member>
<SSN>2222222222</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="CCC">
</Benefit>
<Benefit BenefitType="DDD">
</Benefit>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000003</ContractID>
<Member>
<SSN>333333333</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="CCC">
</Benefit>
</Benefits>
</Member>
</Contract>
</Contracts>
</Sponsor>
<Sponsor>
<SponsorID>TEST02</SponsorID>
<Contracts>
<Contract>
<ContractID>0000011</ContractID>
<Member>
<SSN>1111111111</SSN>
<Gender>M</Gender>
<Benefits>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000002</ContractID>
<Member>
<SSN>2222222222</SSN>
<Gender>F</Gender>
<Benefits>
</Benefits>
</Member>
</Contract>
</Contracts>
</Sponsor>
</Sponsors>
</Sender>
我想获取合约节点的所有信息,以及父节点的SponsorID。下面是使用 XmlReader 部分读取 xml 文件的代码:
static IEnumerable<XElement> SimpleStreamAxis(string inputUrl, string elementName)
{
using (XmlReader reader = XmlReader.Create(inputUrl))
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == elementName)
{
XElement el = XNode.ReadFrom(reader) as XElement;
if (el != null)
{
yield return el;
}
}
}
}
}
}
问题来了。我不能用这个,因为整个赞助商树可能对内存来说太大了。
var sponsor = SimpleStreamAxis(file, "Sponsor");
我也不能用这个,因为我不能只用合同节点信息告诉 SponsorID。
var contract = SimpleStreamAxis(file, "Contract");
有什么方法可以读取Sponsor中的SponsorID,向前移动光标,读取该Sponsor下的所有Contract节点,然后移动到下一个Sponsor读取SponsorID及其Contract节点等等?
是的,这可以完成 假设 SponsorID
总是先于 Contract
个节点 。
基本思路是通读 XML 文件,直到找到具有所需名称 "SponsorID"
或 "Contract"
的元素,然后生成它们以供更高处理
public static IEnumerable<XElement> StreamNamedElements(XmlReader reader, IEnumerable<XName> names)
{
var nameSet = new HashSet<XName>(names);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && nameSet.Contains(XName.Get(reader.LocalName, reader.NamespaceURI)))
{
XElement el = XNode.ReadFrom(reader) as XElement;
if (el != null)
yield return el;
}
}
}
在 SponsorID
始终存在且位于 Contract
之前的情况下,这将正确枚举这些元素。但是,如果赞助商 ID 丢失或顺序错误,则可能会使用之前赞助商的赞助商 ID。可以通过使用 ReadSubtree()
:
将每个“SponsorID
”的范围限制为包含“Sponsor
”的元素来捕获此错误
public static IEnumerable<XmlReader> StreamNamedSubtrees(XmlReader reader, IEnumerable<XName> names)
{
var nameSet = new HashSet<XName>(names);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && nameSet.Contains(XName.Get(reader.LocalName, reader.NamespaceURI)))
{
var subReader = reader.ReadSubtree();
yield return subReader;
((IDisposable)subReader).Dispose(); // Be sure to advance to the end of the subtree if the caller did not.
}
}
}
然后像这样使用它:
using (var sr = new StringReader(xml))
using (var reader = XmlReader.Create(sr))
{
foreach (var subReader in StreamNamedSubtrees(reader, new[] { (XName)"Sponsor" }))
{
XElement sponsorID = null;
foreach (var el in StreamNamedElements(subReader, new[] { (XName)"SponsorID", (XName)"Contract" }))
{
if (el.Name == "SponsorID")
{
sponsorID = el;
}
else if (el.Name == "Contract")
{
if (sponsorID == null)
throw new InvalidOperationException();
// Example "higher processing"
Debug.WriteLine(string.Format("{0}: {1}", sponsorID.Value, el.ToString()));
}
}
}
}
试试这个:
using (XmlReader xmlReader = XmlReader.Create("file.xml"))
{
while (xmlReader.Read())
{
if (xmlReader.ReadToFollowing("SponsorID"))
{
string sponsorId = xmlReader.ReadElementContentAsString();
// process SponsorID
Console.WriteLine(sponsorId);
if (xmlReader.ReadToFollowing("Contract"))
{
do
{
XmlReader contractSubtree = xmlReader.ReadSubtree();
XElement contractElement = XElement.Load(contractSubtree);
// process Contract
Console.WriteLine(contractElement.Element("ContractID"));
} while (xmlReader.ReadToNextSibling("Contract"));
}
}
}
}
我有一个非常大的 xml 文件。这是 xml 格式的简化版本。
<?xml version='1.0' encoding='UTF-8'?>
<Sender>
<SenderID>571099948</SenderID>
<Sponsors>
<Sponsor>
<SponsorID>TEST01</SponsorID>
<Contracts>
<Contract>
<ContractID>000001</ContractID>
<Member>
<SSN>1111111111</SSN>
<Gender>M</Gender>
<Benefits>
<Benefit BenefitType="AAA">
</Benefit>
<Benefit BenefitType="BBB">
</Benefit>
</Benefits>
</Member>
<Member>
<SSN>4444444444</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="AAA">
</Benefit>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000002</ContractID>
<Member>
<SSN>2222222222</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="CCC">
</Benefit>
<Benefit BenefitType="DDD">
</Benefit>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000003</ContractID>
<Member>
<SSN>333333333</SSN>
<Gender>F</Gender>
<Benefits>
<Benefit BenefitType="CCC">
</Benefit>
</Benefits>
</Member>
</Contract>
</Contracts>
</Sponsor>
<Sponsor>
<SponsorID>TEST02</SponsorID>
<Contracts>
<Contract>
<ContractID>0000011</ContractID>
<Member>
<SSN>1111111111</SSN>
<Gender>M</Gender>
<Benefits>
</Benefits>
</Member>
</Contract>
<Contract>
<ContractID>0000002</ContractID>
<Member>
<SSN>2222222222</SSN>
<Gender>F</Gender>
<Benefits>
</Benefits>
</Member>
</Contract>
</Contracts>
</Sponsor>
</Sponsors>
</Sender>
我想获取合约节点的所有信息,以及父节点的SponsorID。下面是使用 XmlReader 部分读取 xml 文件的代码:
static IEnumerable<XElement> SimpleStreamAxis(string inputUrl, string elementName)
{
using (XmlReader reader = XmlReader.Create(inputUrl))
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == elementName)
{
XElement el = XNode.ReadFrom(reader) as XElement;
if (el != null)
{
yield return el;
}
}
}
}
}
}
问题来了。我不能用这个,因为整个赞助商树可能对内存来说太大了。
var sponsor = SimpleStreamAxis(file, "Sponsor");
我也不能用这个,因为我不能只用合同节点信息告诉 SponsorID。
var contract = SimpleStreamAxis(file, "Contract");
有什么方法可以读取Sponsor中的SponsorID,向前移动光标,读取该Sponsor下的所有Contract节点,然后移动到下一个Sponsor读取SponsorID及其Contract节点等等?
是的,这可以完成 假设 SponsorID
总是先于 Contract
个节点 。
基本思路是通读 XML 文件,直到找到具有所需名称 "SponsorID"
或 "Contract"
的元素,然后生成它们以供更高处理
public static IEnumerable<XElement> StreamNamedElements(XmlReader reader, IEnumerable<XName> names)
{
var nameSet = new HashSet<XName>(names);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && nameSet.Contains(XName.Get(reader.LocalName, reader.NamespaceURI)))
{
XElement el = XNode.ReadFrom(reader) as XElement;
if (el != null)
yield return el;
}
}
}
在 SponsorID
始终存在且位于 Contract
之前的情况下,这将正确枚举这些元素。但是,如果赞助商 ID 丢失或顺序错误,则可能会使用之前赞助商的赞助商 ID。可以通过使用 ReadSubtree()
:
SponsorID
”的范围限制为包含“Sponsor
”的元素来捕获此错误
public static IEnumerable<XmlReader> StreamNamedSubtrees(XmlReader reader, IEnumerable<XName> names)
{
var nameSet = new HashSet<XName>(names);
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && nameSet.Contains(XName.Get(reader.LocalName, reader.NamespaceURI)))
{
var subReader = reader.ReadSubtree();
yield return subReader;
((IDisposable)subReader).Dispose(); // Be sure to advance to the end of the subtree if the caller did not.
}
}
}
然后像这样使用它:
using (var sr = new StringReader(xml))
using (var reader = XmlReader.Create(sr))
{
foreach (var subReader in StreamNamedSubtrees(reader, new[] { (XName)"Sponsor" }))
{
XElement sponsorID = null;
foreach (var el in StreamNamedElements(subReader, new[] { (XName)"SponsorID", (XName)"Contract" }))
{
if (el.Name == "SponsorID")
{
sponsorID = el;
}
else if (el.Name == "Contract")
{
if (sponsorID == null)
throw new InvalidOperationException();
// Example "higher processing"
Debug.WriteLine(string.Format("{0}: {1}", sponsorID.Value, el.ToString()));
}
}
}
}
试试这个:
using (XmlReader xmlReader = XmlReader.Create("file.xml"))
{
while (xmlReader.Read())
{
if (xmlReader.ReadToFollowing("SponsorID"))
{
string sponsorId = xmlReader.ReadElementContentAsString();
// process SponsorID
Console.WriteLine(sponsorId);
if (xmlReader.ReadToFollowing("Contract"))
{
do
{
XmlReader contractSubtree = xmlReader.ReadSubtree();
XElement contractElement = XElement.Load(contractSubtree);
// process Contract
Console.WriteLine(contractElement.Element("ContractID"));
} while (xmlReader.ReadToNextSibling("Contract"));
}
}
}
}