Как можно разобрать XML в пользовательские классы?

У меня есть строки, содержащие xml, которые мне нужно перебирать, анализировать и создавать экземпляры пользовательских классов для вставки в мою базу данных.

Псевдокод для того, что мне нужно, будет примерно таким:

private List<SiteMapping> ExtractSiteMappingsFromXML(String xmlData)
{
    List<SiteMapping> sitemaps = new List<SiteMapping>();
    // parse xmlData, dynamically instantiating a SiteMapping class for each SiteMapping "record" 
in the xml
    foreach (record rec in xmlData)
    {
        SiteMapping sm = new SiteMapping();
        sm.Id = //current id found in the xml data
        sm.siteName = // current site name found in the xml data
        . . .
        sitemaps.Add(sm);
    }
    return sitemaps;
}

Вызывающий объект ExtractSiteMappingsFromXML() затем перебирает возвращаемый список SiteMapping и вставляет записи в базу данных.

Основываясь на идее, которую я получил отсюда, я думаю, что-то вроде этого может быть возможным:

XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlData);
XmlNodeList _ids = doc.GetElementsByTagName("Id");
XmlNodeList _sitenames = doc.GetElementsByTagName("siteName");
. . . // add an XmlNodeList for each element

И тогда я мог бы перебрать списки XmlNodeLists, что-то вроде:

for (int i = 0; i < _ids.Count; i++)
{
    SiteMapping sm = new SiteMapping();
    sm.Id =_ids[i];
    sm.siteName = _sitenames[i];
    . . . // add the rest
    sitemaps.Add(sm);
}

Это разумно? Будет ли это работать, если один или несколько элементов имеют пустые значения? Итак, если элемент иногда пуст, добавит ли он пустое значение в соответствующий XmlNodeList (это то, что я хотел бы), или он ничего не добавит и, таким образом, создаст несоответствие?

Есть ли, возможно, элегантный способ linqy (LINQ-to-XML) сделать это?

Примечание. Это приложение Compact Framework, поэтому оно страдает от этих ограничений в плане реализации.

ОБНОВИТЬ

Я подумал, может быть, этот код:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(omnivore);
List<SiteQuery> sitequeries =
  (from sitequery in xmlDoc.Descendants("SiteQuery")
   select new SiteQuery
   {
       Id = sitequery.Element("Id").Value,
       UPC_PackSize = sitequery.Element("UPC_PackSize").Value,
       UPC_Code = sitequery.Element("UPC_Code").Value,
   }).ToList<SiteQuery>();

... который я адаптировал отсюда, сделал бы свое дело, но я получаю: "Никакая перегрузка для метода" Потомки "не требует 1 аргумента"

ОБНОВЛЕНИЕ 2

Я попробовал это (XDocument вместо XmlDocument):

XDocument xmlDoc = new XDocument();
XDocument.Parse(omnivore);
List<SiteQuery> sitequeries =
 (from sitequery in xmlDoc.Descendants("SiteQuery")
  select new SiteQuery
  {
      Id = Convert.ToInt32(sitequery.Element("Id").Value),
      UPC_PackSize = Convert.ToInt32(sitequery.Element("UPC_PackSize").Value),
      UPC_Code = sitequery.Element("UPC_Code").Value
  }).ToList<SiteQuery>();

Мне показалось странным, что я должен был использовать "XDocument.Parse (omnivore);" вместо "xmlDoc.Parse (omnivore);", но компилятор сообщил мне, что это необходимо...?!?

Неудивительно, что после выполнения этого кода у sitequeries было 0, но...

ОБНОВЛЕНИЕ 3

Возможно, код Нитина Аггарвала будет работать (он компилируется), но во время выполнения я получаю:

System.InvalidOperationException was unhandled
  _HResult=-2146233079
  _message=There is an error in XML document (1, 2).
  HResult=-2146233079
  IsTransient=false
  Message=There is an error in XML document (1, 2).
  Source=System.Xml
  StackTrace:
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader). . .

Может быть, просто XML плохой; но также, я не знаю, доступны ли мне эти классы Jet-Age в Compact Framework (я собираю его в тестовом приложении.NET 4.5.1).

ОБНОВЛЕНИЕ 4

Вишал, чтобы ответить на твой вопрос, вот XML, который я пытаюсь разобрать:

<ArrayOfSiteQuery xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/CStore.DomainModels.HHS"><SiteQuery><Id>00006000002</Id><UPCPackSize>1</UPCPackSize><UPC_Code>00006000002</UPC_Code><crvId></crvId><dept>8</dept><description>ZZ</description><openQty>0.0</openQty><packSize>1</packSize><subDept>80</subDept><unitCost>1.25</unitCost><unitList>5.0</unitList><vendorId>CONFLICT</vendorId><vendorItem>123456</vendorItem></SiteQuery>
.  . . (beaucoup other SiteQuery "records")
<SiteQuery><Id>5705654</Id><UPCPackSize>1</UPCPackSize><UPC_Code>5705654</UPC_Code><crvId></crvId><dept>2</dept><description>what do you want</description><openQty>0.0</openQty><packSize>1</packSize><subDept>0</subDept><unitCost>0.55</unitCost><unitList>1.62</unitList><vendorId></vendorId><vendorItem></vendorItem></SiteQuery></ArrayOfSiteQuery>

Нужно ли сначала вырезать предварительные биты (<ArrayOfSiteQuery xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/CStore.DomainModels.HHS">) и "закрывающий тег" в конце ()?

Кстати, "CStore.DomainModels.HHS" находится в приложении сервера, и клиент, предположительно, не имеет ни малейшего представления, что это такое.

ОБНОВЛЕНИЕ 5

Посмотрев xml в строке, я обнаружил, что его содержимое не соответствует моему пользовательскому классу (это были те же данные, но некоторые имена членов отличались друг от друга, и они были не в порядке друг с другом), поэтому я изменил пользовательский класс, соответствующий xml:

public class SiteQuery
{
    public int Id { get; set; }
    public int UPCPackSize { get; set; }
    public String UPC_Code { get; set; }
    public String crvId { get; set; }
    public int dept { get; set; }
    public String description { get; set; }
    public Double openQty { get; set; }
    public int packSize { get; set; }
    public int subDept { get; set; }
    public Decimal unitCost { get; set; }
    public Decimal unitList { get; set; }
    public String vendorId { get; set; }
    public String vendorItem { get; set; }
}

... но я все еще получаю то же исключение InvalidOp...

ОБНОВЛЕНИЕ 6

Даже после того, как я исключил из XML-кода преамбулу и пост-букву, чтобы она содержала только "xml-записи" SiteQuery, сохранил их в виде файла и загрузил для обработки:

String testData = File.ReadAllText("siteQueryTest.txt");
XmlSerializer serializer = new XmlSerializer(typeof(List<SiteQuery>));
XmlReader reader = XmlReader.Create(new StringReader(testData));
List<SiteQuery> siteQueries;
siteQueries = (List<SiteQuery>)serializer.Deserialize(reader);

... я все еще получаю ошибку времени выполнения:

System.InvalidOperationException was unhandled
  _HResult=-2146233079
  _message=There is an error in XML document (1, 2).
  HResult=-2146233079
  IsTransient=false
  Message=There is an error in XML document (1, 2).
  Source=System.Xml
  StackTrace:
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
       at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
       at Sandbox.Form1.button56_Click(Object sender, EventArgs e) in c:\HoldingTank\Sandbox\Form1.cs:line 2061
    . . .
       StackTrace:
            at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderList1.Read3_ArrayOfSiteQuery()
       InnerException: 

Как это может быть? Содержимое строки "testData":

<SiteQuery><Id>00006000002</Id><UPCPackSize>1</UPCPackSize><UPC_Code>00006000002</UPC_Code><crvId></crvId><dept>8</dept><description>ZZ</description><openQty>0.0</openQty><packSize>1</packSize><subDept>80</subDept><unitCost>1.25</unitCost><unitList>5.0</unitList><vendorId>CONFLICT</vendorId><vendorItem>123456</vendorItem></SiteQuery>
. . . // a ton of other StieQuery records
<SiteQuery><Id>5705654</Id><UPCPackSize>1</UPCPackSize><UPC_Code>5705654</UPC_Code><crvId></crvId><dept>2</dept><description>what do you want</description><openQty>0.0</openQty><packSize>1</packSize><subDept>0</subDept><unitCost>0.55</unitCost><unitList>1.62</unitList><vendorId></vendorId><vendorItem></vendorItem></SiteQuery>

Как может быть "ошибка в документе XML (1, 2)"?

Строка 1, столбец 2 - "S"; что случилось с "S"? Я ничего не предполагаю, так что он ожидает, так как он также не нравится "А" (от <ArrayOfSiteQuery)?

ОБНОВЛЕНИЕ 7

Я добавил:

<?xml version="1.0" encoding="UTF-8"?>

... в файл, и я получаю ту же ошибку, но теперь она на 1,40 (все еще "S" в первом "<SiteQuery>").

1 ответ

Решение

Вы можете попробовать это:

           XmlSerializer serializer = new XmlSerializer(typeof(List<SiteMapping>)); 
            XmlReader reader = XmlReader.Create(new StringReader(xmlData));
            List<SiteMapping> siteMappings;
            siteMappings = (List<SiteMapping>)serializer.Deserialize(reader);

Пожалуйста, дайте мне знать, если это работает

Другие вопросы по тегам