Как преобразовать XML без атрибутов в свойства C#? - PullRequest
0 голосов
/ 28 мая 2020

У меня проблемы с преобразованием моего XML в C# объект.

Мой XML выглядит так:

<metadata>
<item name="Date" type="xs:date"/>
<item name="Hour" type="xs:time"/>
<item name="Group" type="xs:string" length="12"/>
<item name="Status" type="xs:string" length="18"/>
</metadata>
<data>
<row>
<value>2020-05-08</value>
<value>14:00:01</value>
<value>A</value>
<value>Active</value>
</row>
<row>
<value>2020-05-08</value>
<value>14:00:01</value>
<value>B</value>
<value>Inactive</value>
</row>
</data>
</metadata>

Я хочу сопоставить XML объекту c#, который выглядит так

public class GroupDto

    {
        public string Date { get; set; }
        public string Hour { get; set; }
        public string Group { get; set; }
        public string Status { get; set; }
    }

(дата и час могут быть строкой, без проблем) Я использую JsonConvert.SerializeXmlNode (do c), но он дает Мне этот результат:

   "data": {
      "row": [
         {
            "value": [
               "2020-05-08",
               "14:00:01",
               "A",
               "Active"
            ]
         },
....

Мне нравится сопоставлять «значения» с соответствующими свойствами объекта C#. Есть ли способ добиться этого с помощью XML в этом странном формате?

Ответы [ 2 ]

1 голос
/ 28 мая 2020

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

var metadata = doc.Root.Element("metadata");

var mapper = metadata.Elements("item")
    .Select((i, c) => new { Property = i.Attribute("name").Value, Index = c })
    .ToDictionary(i => i.Property, i => i.Index);

var rows = doc.Root.Element("data").Elements("row").Select(r => new GroupDto
{
    Date = r.Elements("value").ElementAt(mapper[nameof(GroupDto.Date)]).Value,
    Hour = r.Elements("value").ElementAt(mapper[nameof(GroupDto.Hour)]).Value,
    Group = r.Elements("value").ElementAt(mapper[nameof(GroupDto.Group)]).Value,
    Status = r.Elements("value").ElementAt(mapper[nameof(GroupDto.Status)]).Value,
}).ToList();

Возможно, это не самое оптимальное решение, но оно дает вам то, что вы хотите -сложный способ.

Вдобавок ко всему, если ваши свойства в вашем xml не будут соответствовать свойствам вашего объекта DTO, вы всегда можете ввести дополнительный слой сопоставления. Например:

var metaMapper = metadata.Elements(ns + "item")
    .Select((i, c) => new { Property = i.Attribute("name").Value, Index = c })
    .ToDictionary(i => i.Property, i => i.Index);

var metaPropertyMapper = new Dictionary<string, string>
{
    {  nameof(GroupDto.Date),  "DateInXML" },
    {  nameof(GroupDto.Hour),  "HourInXML" },
    {  nameof(GroupDto.Group), "GroupInXML" },
    {  nameof(GroupDto.Status), "StatusInXML" },
};

var rows = doc.Root.Element(ns + "data").Elements(ns + "row").Select(r => new GroupDto
{
    Date = r.Elements(ns + "value").ElementAt(metaMapper[metaPropertyMapper[nameof(GroupDto.Date)]]).Value,
    Hour = r.Elements(ns + "value").ElementAt(metaMapper[metaPropertyMapper[nameof(GroupDto.Hour)]]).Value,
    Group = r.Elements(ns + "value").ElementAt(metaMapper[metaPropertyMapper[nameof(GroupDto.Group)]]).Value,
    Status = r.Elements(ns + "value").ElementAt(metaMapper[metaPropertyMapper[nameof(GroupDto.Status)]]).Value,
}).ToList();
1 голос
/ 28 мая 2020

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

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

  • Дата и Час могут быть проанализированы в DateTime
  • Час содержит ':', а Дата - нет.
  • либо Группа только буква или статус соответствует списку известных статусов
foreach(var row in data.Row){
    var temp = new GroupDto();
    foreach(var val in row.Value){

        if( DateTime.TryParse(val, out DateTime date) )
        {
            if(val.Contains(':')) //it should be a time
            {
                temp.Hour = date.ToShortTimeString(); //or keep the datetime format
            }
            else
            {
                temp.Hour = date.ToShortDateString();
            }           
        }

        //either based status on a list of know status
        else if(knowStatus.Contains(val))
        {
            temp.Status = val;
        }       
        //or base Group detection on string lenght and default the remaining possibility to status
        else if(val.lenght>1)
        {       
            temp.Status = val;
        }

        else {      
            temp.Group  = val;
        }
    }
    // return temp/ yeild return temp/ Add it to a list of result etc
}

Обратите внимание, что это более или менее псевдокод, основанный на недопустимом Xml. Это выглядит так, как если бы вы добавили общий root и удалили последнюю закрывающую мета в конце ...:

[XmlRoot(ElementName="row")]
public class Row {
    [XmlElement(ElementName="value")]
    public List<string> Value { get; set; }
}

[XmlRoot(ElementName="data")]
public class Data {
    [XmlElement(ElementName="row")]
    public List<Row> Row { get; set; }
}

Если вы доверяете порядку свойств, вы можете просто

Data.Row.Select(x => new GroupDto{
                Date = x.Value[0],
                Hour = x.Value[1],
                Group = x.Value[2],
                Status = x.Value[3],
            } )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...