C# 组合XML节点
我正在寻找一些帮助,了解如何组合XML文件两个独立部分中的节点。这个想法是,将有一个带有默认信息的部分,另一个部分可以添加更多信息或删除一些默认信息。下面是一个它看起来像什么的例子C# 组合XML节点,c#,xml,data-structures,C#,Xml,Data Structures,我正在寻找一些帮助,了解如何组合XML文件两个独立部分中的节点。这个想法是,将有一个带有默认信息的部分,另一个部分可以添加更多信息或删除一些默认信息。下面是一个它看起来像什么的例子 我的想法是,我可以将产品与类别/问题分开定义,因为这些信息有很多重叠。但是,有些产品需要有稍微不同的类别或问题。下面是它之后的样子 产品A 第一类 标准发行 高级问题 第2类 产品B 第一类 标准发行 高级问题 第2类 特刊 产品C 第2类 特殊类别 秘密问题 产品D 第一类 高级问题 复杂问题 第2类 我可以
我的想法是,我可以将产品与类别/问题分开定义,因为这些信息有很多重叠。但是,有些产品需要有稍微不同的类别或问题。下面是它之后的样子
产品A
第一类
标准发行
高级问题
第2类
产品B
第一类
标准发行
高级问题
第2类
特刊
产品C
第2类
特殊类别
秘密问题
产品D
第一类
高级问题
复杂问题
第2类
我可以使用一组for循环来迭代信息,但是,我正在尝试看看是否有更优雅的方法来实现这一点
PS-现在只需按原样输出信息就可以了。我不想编辑XML本身,因为它只是程序开始时的一次性加载。我将添加一些类或结构来表示这些数据。您试图设置的复杂数据结构并不是简化XML的最佳方法。最终,在尝试读取数据文件时需要花费相当多的精力,保存它可能会非常烦人 需要考虑的几点:
- 如何保存数据
- 如果您的一半产品突然需要一个新的标准问题或新类别,您当时是决定将其添加到一半节点,还是将其添加到默认数组并将删除指令添加到product.category或product.category.issues节点
- 删除指令真的是您需要/想要添加的要求吗
- 如果你想以一个局外人的身份“阅读”数据库,你会自己理解它的结构吗(我发现这很难,而且只有4种产品)
它将简化对节点的读取,使其更具可读性(从人的角度来看也是如此),在长期内更易于维护(面对它,数据结构会保持原来设计的状态多少次?),并且可以将产品的类别与问题分开
我明确地从每个产品中删除了空的Category 2选项,因为它们不需要这种结构(imho,这里的问题很重要)
下面是一个实现,它告诉我们要真正阅读原始xml需要付出多少努力
原创
我试图创建一个优雅的阅读器设计,但这最终只会对您有所帮助,这取决于此处发布的简化数据结构在多大程度上符合实际需求
作为基础,我为数据创建了一些类,我使用抽象类来提供Name&Remove属性,以便更容易“概括”读者,尽管需要一些特殊的实现
[XmlRoot("data")]
public class Data
{
[XmlArray("products")]
[XmlArrayItem("product")]
public Product[] Products { get; set; }
[XmlArray("categories")]
[XmlArrayItem("category")]
public Category[] Categories { get; set; }
}
public abstract class AbstractNamedNode
{
[XmlAttribute("name")]
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is AbstractNamedNode)
{
return string.Equals(((AbstractNamedNode)obj).Name, this.Name);
}
return base.Equals(obj);
}
public override int GetHashCode()
{
if (string.IsNullOrEmpty(Name))
{
return base.GetHashCode();
}
return Name.GetHashCode();
}
public override string ToString()
{
return string.Format("{0}", Name);
}
public virtual T CloneBasic<T>()
where T: AbstractNamedNode, new()
{
T result = new T();
result.Name = this.Name;
return result;
}
}
public abstract class AbstractNamedRemovableNode : AbstractNamedNode
{
[XmlAttribute("remove")]
public bool Remove { get; set; }
public override T CloneBasic<T>()
{
var result = base.CloneBasic<T>() as AbstractNamedRemovableNode;
result.Remove = this.Remove;
return result as T;
}
}
public class Product : AbstractNamedNode
{
[XmlElement("category")]
public Category[] Categories { get; set; }
}
public class Category : AbstractNamedRemovableNode
{
[XmlElement("issue")]
public Issue[] Issues { get; set; }
}
public class Issue : AbstractNamedRemovableNode
{
// intended blank
}
此时(当尝试解析数据时未发生异常),dataFromXML将包含所有已定义的产品(及其各自的类别和问题)和类别节点
要将其读入最终设置,您必须比较每个类别(并克隆其信息),然后再将结果集与标准集进行一次比较(以查看仍应添加产品中未定义的类别)
对于此实现,此数据提供程序将从原始dataFromXML文件返回一组组合的克隆产品。通过依赖抽象类(使用通用规范),可以稍微压缩所有循环,只是它变得更难阅读
public static class DataProvider
{
private static T GetMatchingItem<T, K>(T[] SourceArray, K MatchingNode)
where T: AbstractNamedRemovableNode
where K: AbstractNamedRemovableNode
{
if (SourceArray == null || SourceArray.Length == 0 || MatchingNode == null)
{
return null;
}
var query = from i in SourceArray
where !i.Remove && i.Equals(MatchingNode)
select i;
return query.SingleOrDefault();
}
private static T[] CombineArray<T>(T[] sourceArray, T[] baseArray)
where T : AbstractNamedRemovableNode, new()
{
IList<T> results = new List<T>();
if (sourceArray != null)
{
foreach (var item in sourceArray)
{
if (item.Remove)
{
continue;
}
T copy = default(T);
copy = item.CloneBasic<T>();
if (copy is Category)
{
Category category = copy as Category;
Category original = item as Category;
Category matching = GetMatchingItem(baseArray, item) as Category;
if (matching != null)
{
category.Issues = CombineArray(original.Issues, matching.Issues);
}
else
{
category.Issues = CombineArray(original.Issues, null);
}
}
results.Add(copy);
}
}
if (baseArray != null)
{
foreach (var item in baseArray)
{
if (results.Contains(item))
{
continue;
}
if (sourceArray != null && sourceArray.Contains(item))
{
// the remove option would have worked here
continue;
}
T copy = item as T;
if (copy is Category)
{
Category category = copy as Category;
Category original = item as Category;
category.Issues = CombineArray(original.Issues, null);
}
results.Add(copy);
}
}
return results.OrderBy((item) => item.Name).ToArray();
}
public static Product[] GetCombinedProductInfoFromData(Data data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
IList<Product> products = new List<Product>();
if (data.Products != null)
{
foreach (var originalProduct in data.Products)
{
Product product = originalProduct.CloneBasic<Product>();
if (originalProduct.Categories != null && originalProduct.Categories.Length > 0)
{
product.Categories = CombineArray(originalProduct.Categories, data.Categories);
}
else
{
product.Categories = CombineArray(data.Categories, null);
}
products.Add(product);
}
}
return products.ToArray();
}
}
然而,结果将是一个克隆产品列表,默认类别与指定类别组合克隆,类别中的问题也是如此
我想指出的唯一一件事是,仔细考虑这是否真的是您想要构建数据的方式。特别是“删除”指令是b***;)
public static class DataProvider
{
private static T GetMatchingItem<T, K>(T[] SourceArray, K MatchingNode)
where T: AbstractNamedRemovableNode
where K: AbstractNamedRemovableNode
{
if (SourceArray == null || SourceArray.Length == 0 || MatchingNode == null)
{
return null;
}
var query = from i in SourceArray
where !i.Remove && i.Equals(MatchingNode)
select i;
return query.SingleOrDefault();
}
private static T[] CombineArray<T>(T[] sourceArray, T[] baseArray)
where T : AbstractNamedRemovableNode, new()
{
IList<T> results = new List<T>();
if (sourceArray != null)
{
foreach (var item in sourceArray)
{
if (item.Remove)
{
continue;
}
T copy = default(T);
copy = item.CloneBasic<T>();
if (copy is Category)
{
Category category = copy as Category;
Category original = item as Category;
Category matching = GetMatchingItem(baseArray, item) as Category;
if (matching != null)
{
category.Issues = CombineArray(original.Issues, matching.Issues);
}
else
{
category.Issues = CombineArray(original.Issues, null);
}
}
results.Add(copy);
}
}
if (baseArray != null)
{
foreach (var item in baseArray)
{
if (results.Contains(item))
{
continue;
}
if (sourceArray != null && sourceArray.Contains(item))
{
// the remove option would have worked here
continue;
}
T copy = item as T;
if (copy is Category)
{
Category category = copy as Category;
Category original = item as Category;
category.Issues = CombineArray(original.Issues, null);
}
results.Add(copy);
}
}
return results.OrderBy((item) => item.Name).ToArray();
}
public static Product[] GetCombinedProductInfoFromData(Data data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
IList<Product> products = new List<Product>();
if (data.Products != null)
{
foreach (var originalProduct in data.Products)
{
Product product = originalProduct.CloneBasic<Product>();
if (originalProduct.Categories != null && originalProduct.Categories.Length > 0)
{
product.Categories = CombineArray(originalProduct.Categories, data.Categories);
}
else
{
product.Categories = CombineArray(data.Categories, null);
}
products.Add(product);
}
}
return products.ToArray();
}
}
var productList = DataProvider.GetCombinedProductInfoFromData(dataFromXml);
foreach (var product in productList)
{
Console.WriteLine(product);
if (product.Categories == null)
{
continue;
}
foreach (var category in product.Categories)
{
Console.WriteLine("\t{0}", category);
if (category.Issues == null)
{
continue;
}
foreach (var issue in category.Issues)
{
Console.WriteLine("\t\t{0}", issue);
}
}
}