Linq-запрос для получения данных из массива объектов c # - PullRequest
1 голос
/ 28 марта 2011
public struct Parameter 
{
    public Parameter(string name, string type, string parenttype)  
    {        
        this.Name = name;
        this.Type = type;
        this.ParentType = parenttype;        
    }
    public string Name;
    public string Type;
    public string ParentType;
}

В массиве параметров хранятся следующие значения:

Name        Type                 ParentType
-------------------------------------------------------
composite   CompositeType        
isThisTest  boolean              
BoolValue   boolean              CompositeType
StringValue string               CompositeType
AnotherType AnotherCompositeType CompositeType
account     string               AnotherCompositeType
startdate   date                 AnotherCompositeType

Я хочу прочитать это, чтобы построить XML. что-то вроде:

<composite>
    <BoolValue>boolean</BoolValue>
    <StringValue>string</StringValue>
    <AnotherType>
        <account>string</account>
        <startdate>date</startdate>
    </AnotherType>    
<composite>
<isThisTest>boolean</isThisTest>

Я использую следующую логику для чтения значений:

foreach (Parameter parameter in parameters)
{
    sb.Append("        <" + parameter.Name + ">");
    //HERE: need to check parenttype and get all it's child elements
    //
    sb.Append("</" + parameter.Name + ">" + CrLf);
}

Есть ли более простой способ прочитать массив, чтобы получить родителей и их детей? Может быть, используете LINQ? Я до сих пор на .Net 3.5. Оцените любые предложения с примером кода:)

Ответы [ 2 ]

5 голосов
/ 28 марта 2011

Вы можете написать небольшой рекурсивный метод, чтобы справиться с этим:

IEnumerable<XElement> GetChildren ( string rootType, List<Parameter> parameters )
{
    return from p in parameters
        where p.ParentType == rootType
        let children = GetChildren ( p.Type, parameters )
        select  children.Count() == 0 ? 
            new XElement ( p.Name, p.Type ) :
            new XElement ( p.Name, children );
}

Каждый вызов создает Enumerable из XElements , который содержит параметры, чей родитель имеет переданный тип.Выбор повторяется в методе, снова находя дочерние элементы для каждого элемента.

Обратите внимание, что это предполагает, что данные сформированы правильно.Если два параметра имеют друг друга в качестве родителя, вы получите переполнение стека.

Волшебство заключается в классе XElements (Linq to Xml), который принимает перечисляемые элементы XElements для построения дерева, подобного структуре Xml.

При первом вызове передайте значение null (или используйте параметры по умолчанию, если используете C # 4) в качестве rootType.Используйте как:

void Main()
{
    var parameters = new List<Parameter> {
        new Parameter {Name = "composite", Type = "CompositeType" },
        new Parameter {Name = "isThisTest", Type = "boolean" },
        new Parameter {Name = "BoolValue", Type = "boolean", ParentType = "CompositeType" },
        new Parameter {Name = "StringValue", Type = "string", ParentType = "CompositeType" },
        new Parameter {Name = "AnotherType", Type = "AnotherCompositeType", ParentType = "CompositeType" },
        new Parameter {Name = "account", Type = "string", ParentType = "AnotherCompositeType" },
        new Parameter {Name = "startdate", Type = "date", ParentType = "AnotherCompositeType" }
    };

    foreach ( var r in GetChildren ( null, parameters ) )
    {
        Console.WriteLine ( r );
    }

}

Вывод:

<composite>
  <BoolValue>boolean</BoolValue>
  <StringValue>string</StringValue>
  <AnotherType>
    <account>string</account>
    <startdate>date</startdate>
  </AnotherType>
</composite> 
<isThisTest>boolean</isThisTest>

Редактировать

В ответ на ваш комментарий XElement дает вам две возможности для вывода в видестрока.

ToString () будет выводить форматированный Xml.

ToString (SaveOptions) позволяет указать форматированный или неотформатированный вывод, а такжеисключение повторяющихся пространств имен.

Я уверен, что вы, вероятно, могли бы адаптировать решение для использования StringBuilder, если бы вам это действительно было нужно, хотя, вероятно, это было бы не так элегантно ..

1 голос
/ 28 марта 2011

Похоже, вы хотите использовать рекурсивный метод, например:

string GetChildren(Parameter param, string indent)
{
    StringBuilder sb = new StringBuilder();
    if (HasChildren(param))
    {
        sb.AppendFormat("{0}<{1}>{2}", indent, param.Name, Environment.NewLine);
        foreach (Parameter child in parameters.Where(p => p.ParentType == param.Type))
        {
            sb.Append(GetChildren(child, indent + "   "));
        }
        sb.AppendFormat("{0}</{1}>{2}", indent, param.Name, Environment.NewLine);
    }
    else
    {
        sb.AppendFormat("{0}<{1}>{2}</{1}>{3}", indent, param.Name, param.Type, Environment.NewLine);
    }
    return sb.ToString();
}

Метод, который проверяет, есть ли у параметра дочерние узлы, будет выглядеть так:

bool HasChildren(Parameter param)
{
    return parameters.Any(p => p.ParentType == param.Type);
}

Коллекция parameters может быть определена как IEnumerable<Parameter> и может быть реализована с использованием List<Parameter>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...