从表示嵌套属性的字符串列表构造XML

时间:2016-01-15 09:54:15

标签: c# xml algorithm

我正在尝试构造一个表示对象的XML,但只包含两个对象之间已修改的属性,以及第二个对象中的值。我不知道类的结构,但我知道我将永远有2个同一类的对象。

示例:

public class A
{
    public B Property_A_B { get; set; }
    public C Property_A_C { get; set; }
}

public class B
{
    public int Property_B_Int { get; set; }
    public string Property_B_String { get; set; }
}

public class C
{
    public bool Property_C_Bool { get; set; }
    public D Property_C_D { get; set; }
}

public class D
{
    public double Property_D_Double { get; set; }
}

我有2个A类型的对象。如果属性Property_B_IntProperty_B_StringProperty_D_Double在我的2个对象之间不相同,我有一个字符串列表,其中包含:

"A.Property_A_B.Property_B_Int"
"A.Property_A_B.Property_B_String"
"A.Property_A_C.Property_C_D.Property_D_Double"

使用这3个字符串,我必须构造该XML:

<A>
    <Property_A_B>
        <Property_B_Int>12345</Property_B_Int>
        <Property_B_String>Hello world</Property_B_String>
    </Property_A_B>
    <Property_A_C>
        <Property_C_D>
            <Property_D_Double>456.76</Property_D_Double>
        </Property_C_D>
    </Property_A_C>
</A>

对象可以有许多不同的结构和dephts。我只知道它们的类型和不同属性的名称。该函数必须适用于任何对象。

我写了那段代码:

XmlDocument xml = new XmlDocument();
using (MemoryStream ms = new MemoryStream()) {
    using (XmlWriter writer = XmlWriter.Create(ms)) {
        // Début du fichier
        writer.WriteStartDocument();
        // Début de l'objet
        writer.WriteStartElement(Objet_Fin.GetType().Name);
        // Ecriture des champs modifiés
        foreach (Difference diff in Differences) {
            string[] composants_diff = diff.PropertyName.Split({ "." }, StringSplitOptions.RemoveEmptyEntries);
            object sous_objet = Objet_Fin;
            Type type_sous_objet = null;
            PropertyInfo sous_propriete = default(PropertyInfo);
            foreach (string composant_diff in composants_diff) {
                // Pour chaque itération, on navigue vers la propriété suivante
                type_sous_objet = sous_objet.GetType();
                sous_propriete = type_sous_objet.GetProperty(composant_diff);
                sous_objet = sous_propriete.GetValue(sous_objet);
                // On ouvre un noeud XML pour chaque propriété passée
                writer.WriteStartElement(composant_diff);
            }
            writer.WriteValue(sous_objet.ToString());
            foreach (string composant_diff in composants_diff) {
                // On ferme chaque noeud ouvert
                writer.WriteEndElement();
            }
        }
        // Fin de l'objet
        writer.WriteEndElement();
        // Fin du fichier
        writer.WriteEndDocument();
        // Ecriture dans le flux
        writer.Flush();
    }
    // Ecriture du contenu du flux dans le XmlDocument
    ms.Position = 0;
    xml.Load(ms);
}

它几乎可以工作,但它会多次生成相同的对象属性,而不是一个。这样的事情:

<A>
    <Property_A_B>
        <Property_B_Int>12345</Property_B_Int>
    </Property_A_B>
    <Property_A_B>
        <Property_B_String>Hello world</Property_B_String>
    </Property_A_B>
    <Property_A_C>
        <Property_C_D>
            <Property_D_Double>456.76</Property_D_Double>
        </Property_C_D>
    </Property_A_C>
</A>

我不知道如何正确生成XML,每个对象属性只写一次。你能帮帮我吗?感谢。

2 个答案:

答案 0 :(得分:1)

这样做的唯一正确方法是使用递归。我使用StringReader()进行测试,可以很容易地将其修改为StreamReader()。我添加了一些非常严格的测试数据来验证代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string input1 = "a";
            List<XElement> results1 = ProcessData(input1);

            string input2 =
                "A.Property_A_B.Property_B_Int\n" +
                "A.Property_A_B.Property_B_String\n" +
                "A.Property_A_C.Property_C_D.Property_D_Double";

            List<XElement> results2 = ProcessData(input2);

            string input3 =
                "a.a.a.a.a.a.a\n" +
                "a.a.a.a.a.a.b\n" +
                "a.a.a.b";

            List<XElement> results3 = ProcessData(input3);

        }
        static List<XElement> ProcessData(string input)
        {

            StringReader reader = new StringReader(input);
            string inputLine = "";
            List<List<string>> properties = new List<List<string>>();

            while ((inputLine = reader.ReadLine()) != null)
            {
                properties.Add(inputLine.Split(new char[] { '.' }).ToList());
            }
            List<XElement> results = Recursive(properties);

            return results;
        }

        static List<XElement> Recursive(List<List<string>> input)
        {
            List<XElement> results = new List<XElement>();
            string parent = input[0][0];

            Dictionary<string, List<List<string>>> dict = input.GroupBy(m => m.FirstOrDefault(), n => n)
                .ToDictionary(m => m.Key, n => n.Select(p => p.Skip(1).ToList<string>()).ToList());
            foreach (string key in dict.Keys)
            {
                List<List<string>> subChilds = dict[key];
                //List<XElement> subElements = new List<XElement>();
                for (int i = subChilds.Count() - 1; i >= 0;  i--)
                {
                    if (subChilds[i].Count() == 0)
                    {
                        subChilds.RemoveAt(i);
                    }
                }
                List<XElement> child = null;
                if (subChilds.Count() > 0)
                {
                    child = Recursive(subChilds);
                    //elements.Add(child);

                }
                results.Add(new XElement(key, child));
            }
            return results;
        }
    }
}

答案 1 :(得分:0)

经过一天的工作,我终于找到了它:

XmlDocument xml = new XmlDocument();
XmlElement rootNode = xml.CreateElement(Objet_Fin.GetType().Name);
xml.AppendChild(rootNode);

// Pour chaque différence
foreach (Difference diff in Differences) {
    string[] composants_diff = diff.PropertyName.Split({ "." }, StringSplitOptions.RemoveEmptyEntries);
    XmlElement parentNode = rootNode;
    XmlElement currentNode = null;
    string currentXPath = "/" + rootNode.Name;
    // Pour chaque propriété imbriquée
    for (i = 0; i <= composants_diff.Length - 2; i++) {
        // Construction du Xpath 
        currentXPath += "/" + composants_diff(i);
        // Selection du node à tester
        currentNode = rootNode.SelectSingleNode(currentXPath);
        if (currentNode == null) {
            // Si le node n'existe pas, on le créé et l'ajoute au parent
            currentNode = xml.CreateElement(composants_diff(i));
            parentNode.AppendChild(currentNode);
        }
        parentNode = currentNode;
    }
    // On écrit la propriété "finale"
    XmlNode newNode = xml.CreateElement(composants_diff.Last());
    newNode.InnerText = diff.Object2Value;
    parentNode.AppendChild(newNode);
}

return xml;

希望有人会在某一天阅读...