求助:如何将指定XML转换为格式化纯文本?附C#尝试代码
解决方案:将XML转换为格式化键值对文本
当然可以实现这个转换需求!你的现有代码只抓取了gls:PatientDem元素的整体拼接文本,没有深入遍历它内部的嵌套子元素,所以才得不到你想要的PatientId: 123455, IdScheme: TEST这类精准的键值对格式。下面是修正后的代码,以及详细的逻辑说明:
问题分析
你原来的代码使用x.Value获取gls:PatientDem的文本,这会把该元素下所有子元素的文本内容直接拼接成一串,完全丢失了各个字段的结构信息。要实现预期格式,我们需要递归遍历XML的所有元素,找到每个叶子节点(没有子元素的节点),然后提取它的层级名称作为键,文本内容作为值。
修正后的C#代码
using System; using System.Text; using System.Xml.Linq; public class XmlToKeyValueConverter { public static void Main() { // 你的XML内容(这里直接嵌入方便测试) string xml = @"<?xml version=""1.0""?> <gls:TheDocument xmlns:pbr=""http://www.something.com"" xmlns:gls=""http://www.testsomething.com"" xmlns:cnr=""http://www.organisation.com"" xmlns:h=""http://www.w3.org/1999/xhtml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" schemaVersion=""2.8""> <gls:PatientDem> <cnr:PatientId> <cnr:IdValue>123455</cnr:IdValue> <cnr:IdScheme>TEST</cnr:IdScheme> <cnr:IdType>PRN</cnr:IdType> </cnr:PatientId> <cnr:PatientName> <cnr:Name> <cnr:Title>Mr</cnr:Title> <cnr:GivenName>Joe</cnr:GivenName> <cnr:FamilyName>Wood</cnr:FamilyName> </cnr:Name> <cnr:NameType>Current Name</cnr:NameType> </cnr:PatientName> <cnr:PatientAddress> <cnr:Address> <cnr:AddressLine>57 High Street</cnr:AddressLine> <cnr:AddressLine>London</cnr:AddressLine> </cnr:Address> <cnr:PostCode>WC1E 7HU</cnr:PostCode> <cnr:AddressType>Current Residence</cnr:AddressType> </cnr:PatientAddress> <cnr:DateOfBirth>1969-11-02</cnr:DateOfBirth> <cnr:Sex>M</cnr:Sex> </gls:PatientDem> <pbr:PatientAdminRef> <pbr:Referrer> <pbr:RefGP> <cnr:GpcpName> <cnr:FullName>DR SMITH</cnr:FullName> </cnr:GpcpName> <cnr:TheOrganisation> <cnr:OrganisationId> <cnr:IdValue>52522</cnr:IdValue> <cnr:IdScheme>PracticeID</cnr:IdScheme> <cnr:IdType>Healthcare Organisation</cnr:IdType> </cnr:OrganisationId> <cnr:OrganisationName>National Health Service</cnr:OrganisationName> <cnr:OrganisationAddress> <cnr:TheAddress> <cnr:AddressLine1>Centre House</cnr:AddressLine1> <cnr:AddressLine2>799 Chichester Street</cnr:AddressLine2> <cnr:AddressLine3>London</cnr:AddressLine3> </cnr:TheAddress> <cnr:AddressType>Practice Address</cnr:AddressType> </cnr:OrganisationAddress> <cnr:OrganisationTelecom> <cnr:TelephoneNumber>016190542350</cnr:TelephoneNumber> <cnr:TeleType>Voice</cnr:TeleType> </cnr:OrganisationTelecom> </cnr:TheOrganisation> </pbr:RefGP> </pbr:Referrer> </pbr:PatientAdminRef> </gls:TheDocument>"; XDocument doc = XDocument.Parse(xml); XElement root = doc.Root; XNamespace glsNs = root.GetNamespaceOfPrefix("gls"); XNamespace cnrNs = root.GetNamespaceOfPrefix("cnr"); XNamespace pbrNs = root.GetNamespaceOfPrefix("pbr"); var output = new StringBuilder(); // 处理患者 demographic 信息 ProcessNestedElements(doc.Descendants(glsNs + "PatientDem").First(), output, ""); // 处理转诊信息 ProcessNestedElements(doc.Descendants(pbrNs + "PatientAdminRef").First(), output, ""); // 输出结果(你也可以把output.ToString()写入文件或返回) Console.WriteLine(output.ToString()); } /// <summary> /// 递归处理嵌套XML元素,提取键值对 /// </summary> /// <param name="element">当前处理的XML元素</param> /// <param name="sb">用于构建输出的StringBuilder</param> /// <param name="parentKeyPrefix">父元素的键前缀(用于生成层级键名)</param> private static void ProcessNestedElements(XElement element, StringBuilder sb, string parentKeyPrefix) { string elementLocalName = element.Name.LocalName; // 生成当前元素的键名(包含父层级,避免重复键) string currentKey = string.IsNullOrEmpty(parentKeyPrefix) ? elementLocalName : $"{parentKeyPrefix}.{elementLocalName}"; // 如果当前元素有子元素,递归处理子元素 if (element.Elements().Any()) { foreach (var childElement in element.Elements()) { ProcessNestedElements(childElement, sb, currentKey); } } else { // 如果是叶子节点(没有子元素),输出键值对 sb.AppendLine($"{currentKey}: {element.Value}"); } } }
代码说明
- 递归遍历逻辑:
ProcessNestedElements方法会逐层深入XML结构,遇到叶子节点(没有子元素的节点)时,就将其层级化的键名和文本值添加到输出中。 - 命名空间处理:正确获取了XML中的各个命名空间(
gls、cnr、pbr),确保能精准定位到目标元素。 - 层级键名:生成的键名包含父元素路径(比如
PatientDem.PatientId.IdValue),避免了重复键名的问题(比如XML中出现两次AddressLine)。
可选调整
如果你不想保留父层级前缀,只想显示最后一级的键名(比如直接显示IdValue: 123455而不是PatientDem.PatientId.IdValue: 123455),可以修改ProcessNestedElements方法的currentKey生成逻辑,只使用elementLocalName即可:
// 修改这一行,去掉父前缀 string currentKey = elementLocalName;
不过这种方式可能会出现重复键名(比如两个AddressLine),需要根据你的需求权衡选择。
内容的提问来源于stack exchange,提问作者KI1
相关产品推荐
相关产品推荐

