转换XML,使其所有元素和属性有效地变成“minOccurs 1”
本文关键字:有效地 minOccurs 属性 XML 元素 转换 | 更新日期: 2023-09-27 18:10:19
我想转换一个复杂的XML元素,使其结构变得"规则"——该元素中任何位置出现的所有子元素和属性都将出现在其100%的节点中。
可能更容易表达我的意思…
样本输入:<product>
<size name="S"/>
<size>
<stock>10</stock>
</size>
</product>
所需输出:<product>
<size name="S">
<stock/>
</size>
<size name="">
<stock>10</stock>
</size>
</product>
事件:
第一个size
元素提供了一个空的子元素stock
(因为第二个size
元素有一个)。
属性/size@name
与一个空值被添加到第二个size
子元素(因为第一个size
元素有一个)。
先决条件:
处理的XML不太可能很大(使用LINQ没有问题,将所有XML缓存到内存中等等)
我事先不知道它的XML模式
下面的代码应该符合您的期望。
使用test.xml文件作为输入
<product xmlns="http://www.example.com/schemas/v0.1">
<size name="S"/>
<size>
<stock>
<a.el a.att="a.value"/>
</stock>
</size>
<size>
<stock>
10
<b.el b.att="b.value"/>
</stock>
</size>
<size size.att="size.value" name="e">
<stock>
12
<b.el b.att2="b.value2"/>
</stock>
</size>
</product>
生成以下有效和规范化的输出
<product xmlns="http://www.example.com/schemas/v0.1">
<size name="S" size.att="">
<stock>
<a.el a.att=""></a.el>
<b.el b.att="" b.att2=""></b.el>
</stock>
</size>
<size name="" size.att="">
<stock>
<a.el a.att="a.value" />
<b.el b.att="" b.att2=""></b.el>
</stock>
</size>
<size name="" size.att="">
<stock>
10
<b.el b.att="b.value" b.att2="" /><a.el a.att=""></a.el></stock>
</size>
<size size.att="size.value" name="e">
<stock>
12
<b.el b.att2="b.value2" b.att="" /><a.el a.att=""></a.el></stock>
</size>
</product>
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XDocument xDoc = XDocument.Load("test.xml");
XNamespace ns = xDoc.Root.Name.Namespace;
var mgr = new XmlNamespaceManager(new NameTable());
mgr.AddNamespace("ns", ns.ToString());
var elements = xDoc.XPathSelectElements("/ns:product/ns:size", mgr);
Descriptor desc = Descriptor.InferFrom(elements);
desc.Normalize(elements);
Console.Write(xDoc.ToString());
}
}
public class Descriptor
{
private readonly IList<XName> _attributeNames = new List<XName>();
private readonly IDictionary<XName, Descriptor> _elementDescriptors = new Dictionary<XName, Descriptor>();
public XName Name { get; private set; }
public IEnumerable<XName> AttributeNames { get { return _attributeNames; } }
public IEnumerable<KeyValuePair<XName, Descriptor>> ElementDescriptors { get { return _elementDescriptors; } }
private void UpdateNameFrom(XElement element)
{
if (Name == null)
{
Name = element.Name;
return;
}
if (element.Name == Name)
return;
throw new InvalidOperationException();
}
private void Add(XAttribute att)
{
XName name = att.Name;
if (_attributeNames.Contains(name))
return;
_attributeNames.Add(name);
}
public static Descriptor InferFrom(IEnumerable<XElement> elements)
{
var desc = new Descriptor();
foreach (var element in elements)
InferFromInternal(element, desc);
return desc;
}
private static void InferFromInternal(XElement element, Descriptor desc)
{
desc.UpdateNameFrom(element);
foreach (var att in element.Attributes())
desc.Add(att);
foreach (var subElement in element.Elements())
desc.Add(subElement);
}
private void Add(XElement subElement)
{
Descriptor desc;
if (_elementDescriptors.ContainsKey(subElement.Name))
desc = _elementDescriptors[subElement.Name];
else
{
desc = new Descriptor();
_elementDescriptors.Add(subElement.Name, desc);
}
InferFromInternal(subElement, desc);
}
public void Normalize(IEnumerable<XElement> elements)
{
foreach (var element in elements)
NormalizeInternal(element);
}
private void NormalizeInternal(XElement element)
{
if (element.Name != Name)
throw new InvalidOperationException();
foreach (var attribute in AttributeNames)
{
var att = element.Attribute(attribute);
if (att != null)
continue;
element.Add(new XAttribute(attribute, string.Empty));
}
foreach (var attribute in _elementDescriptors)
{
XElement el = element.Element(attribute.Key);
if (el == null)
{
el = new XElement(attribute.Key, string.Empty);
element.Add(el);
}
attribute.Value.NormalizeInternal(el);
}
}
}
}