2016-10-14 2 views
1

Я хотел бы иметь возможность возвращать строго типизированные объекты вместо XElements из кода, который передает потоки XML-документа. Скажем XML документ в вопросе:Как вернуть строго типизированные объекты вместо XElements при потоковой передаче документа XML

<?xml version="1.0" encoding="utf-8" ?> 
<People> 
    <Person> 
    <FirstName>John</FirstName> 
    <LastName>Smith</LastName> 
    </Person> 
    <Person> 
    <FirstName>Adam</FirstName> 
    <LastName>Smith</LastName> 
    </Person> 
    <Person> 
    <FirstName>Jane</FirstName> 
    <LastName>Smith</LastName> 
    </Person> 
</People> 

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

public class PeopleReader 
{ 
    public static IEnumerable<XElement> StreamPerson(string path) 
    { 
     using (XmlReader rdr = XmlReader.Create(path)) 
     { 
      rdr.MoveToContent(); 

      while (rdr.Read()) 
      { 
       if (rdr.NodeType == XmlNodeType.Element && rdr.Name == "Person") 
       { 
        XElement item = XElement.ReadFrom(rdr) as XElement; 

        if (item != null) 
         yield return item; 
       } 
      }     
     } 
    } 
} 

текущий код вызова является:

// Current implementation 
foreach (var person in PeopleReader.StreamPerson(@"...\People.xml")) 
{ 
    MessageBox.Show(person.Element("LastName").Value); 
} 

, где я должен позвонить .Element и .Value.

То, что я хотел бы иметь, как вызывающий код является:

// Required implementation where person is returned as a strongly typed obj. 
foreach (var person in PeopleReader.StreamPerson(@"...\People.xml")) 
{ 
    MessageBox.Show(person.LastName); 
} 

Я понимаю, что я могу использовать xsd.exe генерировать файл .xsd генерировать .cs файлы, но я не ясно фактические шаги. Кроме того, я не уверен, как изменить код считывателя, чтобы вернуть генерируемые классы, чтобы вызывающий код получал строго типизированные объекты для перечисления? То есть, как я могу перейти от XElements к строго типизированным классам? Кастинг не кажется вариантом?

Я использую VS2015 и, по возможности, предпочитаю не использовать сторонние инструменты.

Большое спасибо заранее.

+0

Сначала вам понадобятся те сильные типы. Можно ли написать их вручную, или они должны быть сгенерированы? –

+0

Привет @HenkHolterman, не выполнимо сделать вручную. Сгенерировано лучше всего. Я использовал простой пример здесь, чтобы передать свою точку. – Cleve

ответ

1

Я предлагаю вам использовать XmlSerializer.

var serializer = new XmlSerializer(typeof(Person)); 

using (var rdr = XmlReader.Create(path)) 
{ 
    rdr.MoveToContent(); 

    while (rdr.Read()) 
    { 
     if (rdr.NodeType == XmlNodeType.Element && rdr.Name == "Person") 
     { 
      yield return (Person) serializer.Deserialize(rdr); 
     } 
    } 
} 

Где ваш класс определен, как показано ниже:

public class Person 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

См this fiddle для рабочей демонстрации.

Чтобы создать классы для вашего XML, вы можете использовать xsd.exe (для XML или XML-схемы, если таковой имеется), или вы можете скопировать и вставить образец XML в Visual Studio с помощью Edit -> Paste Special -> Paste XML as Classes. См. this related question.

+0

Привет @Charles Mager, да, мой файл требует потоковой передачи. Приведенный выше пример был сокращен, чтобы передать сообщение – Cleve

+0

спасибо за ваше обновление. Очень признателен. – Cleve

+0

Я не нахожу Редактировать -> Специальная вставка -> Вставить XML как классы в моем VS 2015 Professional. Что я делаю не так? – Cleve

1

Вместо возвращения IEnumerable<XElement> создать тип Person и вернуть IEnumerable<Person>:

public class Person 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

И затем возвращает новый экземпляр этого:

public static IEnumerable<XElement> StreamPerson(string path) 
{ 
    using (XmlReader rdr = XmlReader.Create(path)) 
    { 
     rdr.MoveToContent(); 

     while (rdr.Read()) 
     { 
      XElement item = XElement.ReadFrom(rdr) as XElement; 
      if (item != null) 
      { 
       yield return new Person 
       { 
        FirstName = item.Element("FirstName")?.Value, 
        LastName = item.Element("LastName")?.Value 
       }; 
      } 
     } 
    } 
} 

Update - После того, как понимание того, что они xml представляют собой «облегченную» версию более крупных классов и что исходный код для эти классы относятся к текущему проекту, который я предлагаю:

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

public class PersonBase 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

public class Person : PersonBase 
{ 
    public string OtherProperty { get; set; } 
} 

Затем сделать экземпляр использование объекта либо мой подход выше или подход, предложенный Карлом

+0

У вас есть 'if (item! = Null)' без объявления или инициализации переменной 'item', поэтому я думаю, что' XElement item = XElement.ReadFrom (rdr) как XElement; 'is отсутствует в вашем фрагменте –

+0

Привет @Gilad Green, у меня будет много классов, таких как Личность, некоторые из которых будут более сложными, поэтому я хотел опираться на какой-то автоматический подход, который, как я думал, может предоставить xsd? – Cleve

+0

@MartinHonnen - Спасибо, исправлено. Случайно удалили его при копировании остальной части кода из quesiton –

Смежные вопросы