Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 组合XML节点_C#_Xml_Data Structures - Fatal编程技术网

C# 组合XML节点

C# 组合XML节点,c#,xml,data-structures,C#,Xml,Data Structures,我正在寻找一些帮助,了解如何组合XML文件两个独立部分中的节点。这个想法是,将有一个带有默认信息的部分,另一个部分可以添加更多信息或删除一些默认信息。下面是一个它看起来像什么的例子 我的想法是,我可以将产品与类别/问题分开定义,因为这些信息有很多重叠。但是,有些产品需要有稍微不同的类别或问题。下面是它之后的样子 产品A 第一类 标准发行 高级问题 第2类 产品B 第一类 标准发行 高级问题 第2类 特刊 产品C 第2类 特殊类别 秘密问题 产品D 第一类 高级问题 复杂问题 第2类 我可以

我正在寻找一些帮助,了解如何组合XML文件两个独立部分中的节点。这个想法是,将有一个带有默认信息的部分,另一个部分可以添加更多信息或删除一些默认信息。下面是一个它看起来像什么的例子


我的想法是,我可以将产品与类别/问题分开定义,因为这些信息有很多重叠。但是,有些产品需要有稍微不同的类别或问题。下面是它之后的样子

产品A
第一类
标准发行
高级问题
第2类
产品B
第一类
标准发行
高级问题
第2类
特刊
产品C
第2类
特殊类别
秘密问题
产品D
第一类
高级问题
复杂问题
第2类
我可以使用一组for循环来迭代信息,但是,我正在尝试看看是否有更优雅的方法来实现这一点


PS-现在只需按原样输出信息就可以了。我不想编辑XML本身,因为它只是程序开始时的一次性加载。我将添加一些类或结构来表示这些数据。

您试图设置的复杂数据结构并不是简化XML的最佳方法。最终,在尝试读取数据文件时需要花费相当多的精力,保存它可能会非常烦人

需要考虑的几点:

  • 如何保存数据
  • 如果您的一半产品突然需要一个新的标准问题或新类别,您当时是决定将其添加到一半节点,还是将其添加到默认数组并将删除指令添加到product.category或product.category.issues节点
  • 删除指令真的是您需要/想要添加的要求吗
  • 如果你想以一个局外人的身份“阅读”数据库,你会自己理解它的结构吗(我发现这很难,而且只有4种产品)
更新(有关原始实施,请查看更新下方)

我越是考虑这个问题,我就会说,您应该从顶部重新检查您的数据结构

我现在看到的结构很像:

产品->有0个或多个问题

问题->正好有一个类别

类别

因此,在我看来,表示数据的更简单方法是从产品中删除类别标签,并直接在产品下添加问题。然后,这些类别仍然可以位于包含潜在额外信息的单独节点列表中,例如:


它将简化对节点的读取,使其更具可读性(从人的角度来看也是如此),在长期内更易于维护(面对它,数据结构会保持原来设计的状态多少次?),并且可以将产品的类别与问题分开

我明确地从每个产品中删除了空的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);
        }
    }
}