2017-01-30 9 views
0

Я читаю кучу XML-файлов в список (на самом деле IEnumerable) XElements. Затем я хочу преобразовать список XElement (эти XElements содержат кучу дочерних элементов) в список классов, поэтому я могу сделать более поздние операции с данными более легко.Создать список из XElements Динамически

Теперь, если я заранее знаю структуру XElements, это было бы легко; Я просто создаю класс, который имитирует структуру XElement и заполняет его содержимым XElement. Но вот предостережение; моя структура элемента XML-файла в основном похожа, но может существовать нечетный элемент, который имеет другую структуру. Чтобы лучше проиллюстрировать ситуацию, позвольте мне привести пример.

Предположим, что мои файлы XML содержат кучу элементов «Человек». Элементы Person имеют некоторые общие элементы, которые будут во ВСЕХ элементах, но есть некоторые дети Person, которые могут быть найдены только в некоторых элементах.

Например, все элементы Person эти обязательные детей:

<Person> 
    <Name/> 
    <Age/> 
    <City/> 
    <Country/> 
    </Person> 

Но некоторые Person элементы могут содержать дополнительные детей следующим образом:

<Person> 
    <Name/> 
    <Age/> 
    <City/> 
    <Country/> 
    <EyeColor/> 
    <Profession/> 
    </Person> 

Сделать вещи хуже, эти дочерние элементы также могут иметь в основном сходную структуру, которая время от времени меняется.

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

Так что я ищу способ создания экземпляров класса динамически, чтобы соответствовать структуре XElement. Другими словами, я бы очень хотел подражать структуре элементов вплоть до самого глубокого уровня.

Заранее благодарен!

+0

Как правило, причина для XML - это противоположность тому, что происходит. Он должен обеспечить среду для сериализации, которая может быть проверена, как правило, была XSD. Если что-то может просто измениться «на лету» от одной вещи к следующей, что было бы ужасно для проверки данных. Я бы вместо того, чтобы идти по пути того, что у него может не быть, иметь маршрут, где вы получите все возможное, чтобы оно могло быть, а затем сделать сериализацию класса. – djangojazz

+0

Я полностью согласен с вами здесь. Но проблема в том, что я имею дело с системой, которой я не могу ничего изменить. Другими словами, я ничего не могу сделать о структуре файлов XML. Если бы я его разработал, я бы сделал это лучше, но, увы, этого не должно быть. Так что у меня нет выбора, кроме как разобраться с ним, как есть, и найти способ решения проблемы. – Sach

+0

Я не говорю, чтобы изменить структуру, я говорю, может ли кто-нибудь ее предоставить, и XSD, или какой-либо файл проверки? Я бы пошел по маршруту, если сказал, что у вас есть 3 свойства, и у них может быть потенциал на восемь, просто введите класс для восьми свойств. Я приведу пример в ответ. – djangojazz

ответ

1

Я думаю, что лучшим способом было бы получить XSD, если вы не можете это получить, тогда создайте сериализуемый класс, который имеет все возможности, а затем ссылается на это. EG: У вас есть два поля, где иногда устанавливаются и вы никогда не видели набор, но есть потенциал в спецификации где-нибудь, что может случиться.

Итак, давайте составляют симулировать класс:

using System; 
using System.Collections.Generic; 
using System.Xml.Serialization; 

namespace GenericTesting.Models 
{ 
    [Serializable()] 
    public class Location 
    {                     
    [XmlAttribute()] 
    public int Id { get; set; } 
    [XmlAttribute()] 
    public double PercentUsed { get; set; } 
    [XmlElement] 
    public string ExtraGarbage { get; set; } 
    [XmlText] 
    public string UsedOnceInTheUniverse { get; set; } 
    } 
} 

И для целей сериализации/десериализации позвольте мне дать методы расширения для тех, кто:

using System.IO;   
using System.Xml; 
using System.Xml.Serialization; 

namespace GenericTesting 
{         
    static class ExtensionHelper 
    { 
    public static string SerializeToXml<T>(this T valueToSerialize) 
    { 
     dynamic ns = new XmlSerializerNamespaces(); 
     ns.Add("", ""); 
     StringWriter sw = new StringWriter(); 

     using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true })) 
     { 
     dynamic xmler = new XmlSerializer(valueToSerialize.GetType()); 
     xmler.Serialize(writer, valueToSerialize, ns); 
     } 

     return sw.ToString(); 
    } 

    public static T DeserializeXml<T>(this string xmlToDeserialize) 
    { 
     dynamic serializer = new XmlSerializer(typeof(T)); 

     using (TextReader reader = new StringReader(xmlToDeserialize)) 
     { 
     return (T)serializer.Deserialize(reader); 
     } 
    } 
    } 
} 

И простой основной точкой входа в консольное приложение:

static void Main(string[] args) 
{ 
    var locations = new List<Location> 
    { 
     new Location { Id = 1, PercentUsed = 0.5, ExtraGarbage = "really important I'm sure"}, 
     new Location { Id = 2, PercentUsed = 0.6}, 
     new Location { Id = 3, PercentUsed = 0.7}, 
    }; 

    var serialized = locations.SerializeToXml(); 

    var deserialized = serialized.DeserializeXml<List<Location>>(); 

    Console.ReadLine(); 
} 

Я знаю, что это не то, о чем вы просите, но я p Если вы считаете, что наилучшим образом типизированный текст лучше всего подходит для XML, и любая третья сторона, с которой вы когда-либо сталкивались, должна иметь, по крайней мере, некоторый тип спецификации или детали того, что они вам дают.Иначе вы теряете стандарты. Xml не должен создаваться из отражения или другими способами динамически, поскольку это подразумевается, если что-либо требует строгого набора текста, если что-либо.

+0

Я не уверен, что полностью понимаю вас здесь. Мое требование состоит в том, чтобы заполнить данные в нечто похожее на класс «Местоположение», из данных XElement, которые я прочитал из файлов XML. Но из вашего основного(), что я собираю, у вас уже есть данные в классе «Местоположение», а затем вы сериализуете/десериализуете его? Что мне не хватает? – Sach

+0

Если вы просто хотите просто отследить структуру, чем Александр дал прекрасный ответ. Однако вы написали это: «...Я хочу преобразовать список XElement (эти XElements содержат кучу дочерних элементов) в список классов, поэтому я могу сделать более поздние операции с данными более легко ». Это указывает на сериализацию десериализации объектов WELL FORMED. Все, что я предлагаю самым простым способом, - это создать класс для вашего объекта, который вы разбираете. Украсьте его «сериализуемыми атрибутами», а затем создайте метод десериализации. – djangojazz

+0

Я в своем примере дал класс, который у меня уже был из другого примера, и был слишком ленив, чтобы использовать вашу структуру. Но методы «Serialize/Deserialize», которые я использую все время в производственном коде, являются хорошим средством общения с хорошо сформированными классами. Все, что я предлагаю, вам не нужно делать запросы через XElements, а затем бросать их в объекты. Объект может сам по себе выполнять сериализацию десериализации. Кроме того, вам придется обновить класс POCO и класс запросов, с помощью моего метода вы просто обновите класс и украшения POCO. – djangojazz

1

, если вы хотите, чтобы просто перечислить через любой дочерний элемент из <Person> и XML является относительно небольшим вы могли бы использовать LINQ для XML

var listOfElementChildNames = XDocument.Parse(xml).Element("Person") 
                .Elements() 
                .Select(e => e.Name) 
                .ToList(); 

Edit:

вместо выбора .select (е => e.Name) мы могли бы карту к любому классу:

public class Person 
{ 
    public string Name {get;set;} 
    public int Age {get;set;} 
    public string City {get;set;} 
} 

var xml = @"<Person> 
     <Name>John</Name> 
     <Age>25</Age> 
     <City>New York</City> 
     </Person>"; 

var people = XDocument.Parse(xml).Elements("Person") 
    .Select(p => new Person 
     { 
      Name = p.Element("Name").Value, 
      Age = int.Parse(p.Element("Age").Value), 
      City = p.Element("City").Value 
     }).ToList(); 

Mapping result

+0

У меня есть части элементов XML-элементов. Мое требование состоит в том, чтобы преобразовать их в список классов, которые точно имитируют структуру элемента Person. – Sach

+0

Я внес изменения в ответ –

+0

Я думаю, что мы будем кругами здесь, вероятно, недоразумение. Я мог бы использовать это решение только в том случае, если бы заранее знал мои дочерние элементы элемента Person. Но я не знаю, в чем причина этого вопроса. Я знаю «некоторые» дочерних элементов, которые есть у всех элементов Person, но некоторые могут содержать дополнительные дети, которые я не буду знать, пока не прочитаю файлы во время выполнения. Поэтому мое требование состоит в том, чтобы делать то, что вы только что сделали, но для элементов, которые имеют неизвестную структуру во время выполнения. – Sach

1

Позвольте мне сначала извиниться за VB, но это то, что я делаю.

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

Public Class Person 

    Private _dict As New Dictionary(Of String, XElement) 
    Public Sub New(persEL As XElement) 
     'if the class is intended to modify the original XML 
     'use this declaration. 
     Dim aPers As XElement = persEL 
     'if the original XML will go away during the class lifetime 
     'use this declaration. 
     'Dim aPers As XElement =New XElement(persEL) 

     For Each el As XElement In aPers.Elements 
      Me._dict.Add(el.Name.LocalName, el) 
     Next 
    End Sub 

    'mandatory children are done like this 
    Public Property Name() As String 
     Get 
      Return Me._dict("Name").Value 
     End Get 
     Set(ByVal value As String) 
      Me._dict("Name").Value = value 
     End Set 
    End Property 

    Public Property Age() As Integer 
     Get 
      Return CInt(Me._dict("Age").Value) 
     End Get 
     Set(ByVal value As Integer) 
      Me._dict("Age").Value = value.ToString 
     End Set 
    End Property 
    'end mandatory children 

    Public Property OtherChildren(key As String) As String 
     Get 
      Return Me._dict(key).Value 
     End Get 
     Set(ByVal value As String) 
      Me._dict(key).Value = value 
     End Set 
    End Property 

    Public Function HasChild(key As String) As Boolean 
     Return Me._dict.ContainsKey(key) 
    End Function 
End Class 

Вот простой тест, чтобы увидеть, как это работает

Dim onePersXE As XElement = <Person> 
            <Name>C</Name> 
            <Age>22</Age> 
            <Opt1>optional C1</Opt1> 
            <Opt2>optional C2</Opt2> 
           </Person> 

    Dim onePers As New Person(onePersXE) 
    onePers.Name = "new name" 
    onePers.Age = 42 
    onePers.OtherChildren("Opt1") = "new opt1 value" 
    onePers.OtherChildren("Opt2") = "opt 2 has new value" 

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

Вот еще один пример, чтобы показать, как люди могут работать

Dim persons As XElement 
    persons = <persons> 
        <Person> 
         <Name>A</Name> 
         <Age>32</Age> 
        </Person> 
        <Person> 
         <Name>B</Name> 
         <Age>42</Age> 
         <Opt1>optional B1</Opt1> 
         <Opt2>optional B2</Opt2> 
        </Person> 
       </persons> 


    Dim persList As New List(Of Person) 
    For Each el As XElement In persons.Elements 
     persList.Add(New Person(el)) 
    Next 

Надеется, что это по крайней мере дает вам некоторые идеи.

+0

Это не совсем плохая идея, и в некоторых случаях это может быть очень полезно. К сожалению, это не помогает с одним из моих основных требований, который сохраняет структуру XML в классе/словаре или в каком-то таком формате. Спасибо хоть! – Sach

+0

@Sach - если вы используете класс, так как вы будете манипулировать XML напрямую, без изменения самой структуры. Посмотрите на конструктор. – dbasnett