Разбор XML в C# с переменной дочерней структурой

Мне нужно проанализировать XML-файл со следующей структурой: ИЗМЕНИТЬ

<?xml version = "1.0" encoding = "windows-1252" ?>
<TABLE>
    <COMPO>
          <alim_code> 1000 </alim_code>
          <const_code> 56700 </const_code>
          <teneur> 0 </teneur>
          <min missing = " " />
          <max missing = " " />
          <code_confiance> D </code_confiance>
          <source_code missing = "." />
    </COMPO>
    <COMPO>
          <alim_code> 1000 </alim_code>
          <const_code> 60000 </const_code>
          <teneur> 37,5 </teneur>
          <min>         37,3 </min>
          <max>         37,6 </max>
          <code_confiance> D </code_confiance>
          <source_code> 38 </source_code>
    </COMPO>
</TABLE>

Как видите, некоторые поля описываются по-разному, когда значение известно или отсутствует. Я пытался использовать ReadXml () в DataSet, но, похоже, он не работает с «структурой переменных». Похоже, решение состоит в том, чтобы использовать Xdocument и LINQ, но я ничего не знаю о LINQ, и мне не удалось написать рабочий код.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
106
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Linq здесь не поможет. Что вы можете сделать, так это создать DTO для вашего xml, используя образец xml, который содержит все атрибуты и узлы, которые могут быть возвращены в xml.

а затем десериализуйте xml в этот тип.

Ваши классы будут чем-то вроде следующего из создаваемого выше xml:

[XmlRoot(ElementName = "min")]
public class Min {
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
}

[XmlRoot(ElementName = "max")]
public class Max {
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
}

[XmlRoot(ElementName = "source_code")]
public class Source_code {
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
}

[XmlRoot(ElementName = "COMPO")]
public class COMPO {
    [XmlElement(ElementName = "alim_code")]
    public string Alim_code { get; set; }
    [XmlElement(ElementName = "const_code")]
    public string Const_code { get; set; }
    [XmlElement(ElementName = "teneur")]
    public string Teneur { get; set; }
    [XmlElement(ElementName = "min")]
    public Min Min { get; set; }
    [XmlElement(ElementName = "max")]
    public Max Max { get; set; }
    [XmlElement(ElementName = "code_confiance")]
    public string Code_confiance { get; set; }
    [XmlElement(ElementName = "source_code")]
    public Source_code Source_code { get; set; }
}

Теперь вы можете использовать класс XmlSerializer для его десериализации:

XmlSerializer serializer = new XmlSerializer(typeof(List<COMPO>));
List<COMPO> compos = null;
using (var reader = new StringReader(xml))
{
   compos = (List<COMPO>)serializer.Deserialize(reader);
}

Обновлено:

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

[XmlRoot(ElementName = "Table")]
public class Table {
    [XmlElement(ElementName = "COMPO")]
    public List<COMPO> COMPO { get; set; }
}

и теперь соответствующим образом отрегулируйте код десериализации:

XmlSerializer serializer = new XmlSerializer(typeof(Table));
Table table = null;
using (var reader = new StringReader(xml))
{
   compos = (Table)serializer.Deserialize(reader);
}

Я пробовал код, но получаю исключение. Вероятно, это связано с тем, что я не упомянул, что блоки COMPO находятся внутри блока <TABLE> </TABLE> в файле XML.

DrCoolZic 08.04.2018 19:22

вы можете использовать xmltocsharp.azurewebsites.net для создания классов C# для вашего xml

Ehsan Sajjad 08.04.2018 19:25

Я добавил класс Table (созданный с использованием предоставленной вами ссылки) и изменил средство чтения, но все еще получаю ошибку исключения в документе XML (1,1). Неверные данные на уровне корня, строка 1, позиция 1 (это приблизительный перевод!). Файл идет с data.gouv.fr/fr/datasets/… compo_2017 11 21.xml

DrCoolZic 09.04.2018 10:01

убедитесь, что xml действителен, ошибка, похоже, связана с форматом xml

Ehsan Sajjad 09.04.2018 11:26

Похоже, что предоставленный вами код может обрабатывать структуру XML, когда присутствует «отсутствующее» поле, но нет другой формы. поэтому <min missing = "" /> работает, но не <min> 37,3 </min>. Поэтому это решение не работает для моей проблемы.

DrCoolZic 09.04.2018 20:48

этого не должно быть, он отобразит только те свойства, которые доступны

Ehsan Sajjad 09.04.2018 21:16

Извините, это моя ошибка. Я вызывал StringReader () с xmlFileName. После передачи ему содержимого, возвращенного File.ReadAllText (), он теперь работает отлично. Спасибо

DrCoolZic 10.04.2018 09:26

@DrCoolZic не забудьте отметить ответ, если он вам помог :)

Ehsan Sajjad 10.04.2018 09:28

На самом деле я все еще не могу получить минимальные и максимальные значения. Когда используется атрибут "missing", класс Min содержит отсутствующее поле с content = "", а если отсутствует, то отсутствующее поле имеет значение NULL. Хорошо. Однако нет способа получить минимальное значение (например, 37,3 во второй записи предоставленного примера). Я не уверен, что [XmlElement (ElementName = "min")] public Min Min {get; набор; } действительно что-то значит. В отладчике Min содержит только отсутствующее поле, которое может быть нулевым или содержать строку, но нет места для хранения минимального значения ???

DrCoolZic 10.04.2018 18:03

@DrCoolZic, можете ли вы создать демонстрационную скрипку, демонстрирующую проблему?

Ehsan Sajjad 10.04.2018 18:34

Я не понимаю вашу просьбу "демо рабочий пример"? Вы хотите, чтобы я опубликовал код, который использую? В качестве тестового примера я использую очень простой XML-файл, показанный в вопросе. Опять же, проблема с вашим решением заключается в том, что нет места для хранения минимальных / максимальных значений, если они указаны? Или я что-то упускаю? У классов Min и Max есть только одно поле «отсутствует», но нет места для хранения значения.

DrCoolZic 10.04.2018 19:01

Я бы использовал Xml Linq. Есть некоторые элементы, которые могут иметь значение NULL, с которыми вам нужно работать правильно. См. Код ниже

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Globalization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            IFormatProvider provider = CultureInfo.InvariantCulture;

            List<Compo> compos = doc.Descendants("COMPO").Select(x => new Compo() {
                alim_code = (int?)x.Element("alim_code"),
                const_code = (int?)x.Element("const_code"),
                teneur =  (string)x.Element("teneur") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
                min = (string)x.Element("min") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
                max = (string)x.Element("max") == "" ? null : (decimal?)Convert.ToDecimal((string) x.Element("teneur"),provider),
                code_confiance = (string)x.Element("code_confiance"),
                source_code = (string)x.Element("source_code") == "" ? null : (int?)int.Parse(((string)x.Element("source_code")).Trim())
            }).ToList();
        }
    }
    public class Compo
    {
        public int? alim_code {get; set;}
        public int? const_code {get; set;}
        public decimal? teneur {get; set;}
        public decimal? min {get; set;}
        public decimal? max {get; set;}
        public string code_confiance {get; set;}
        public int? source_code { get; set; }
    }
}

Мне сложно понять этот код Linq. Не могли бы вы изменить, чтобы добавить корневой элемент ТАБЛИЦА, который я добавил в описание. Спасибо

DrCoolZic 09.04.2018 10:06

Зачем вам стол. класс? Все, что вам нужно, это List <Compo>. Метод linq Descendants () не требует полного пути, только имя элемента.

jdweng 09.04.2018 11:29

попробовал ваш код, но я получаю исключение с сообщением: неверный формат входной строки, но я понятия не имею, что это значит.

DrCoolZic 09.04.2018 20:55

Я внес изменения в обработку пустых элементов.

jdweng 10.04.2018 02:51

Хорошо, я понял. Я опробую более подходящие типы и дам вам знать. Спасибо

DrCoolZic 10.04.2018 09:29
Ответ принят как подходящий

Рабочее решение на основе предложения Эхсан-Саджад :)

[XmlRoot(ElementName = "min")]
public class Min
{
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
    [XmlText]
    public string Value { get; set; }
}

[XmlRoot(ElementName = "max")]
public class Max
{
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
    [XmlText]
    public string Value { get; set; }
}

[XmlRoot(ElementName = "source_code")]
public class Source_code
{
    [XmlAttribute(AttributeName = "missing")]
    public string Missing { get; set; }
    [XmlText]
    public string Value { get; set; }
}

[XmlRoot(ElementName = "COMPO")]
public class COMPO
{
    [XmlElement(ElementName = "alim_code")]
    public string Alim_code { get; set; }
    [XmlElement(ElementName = "const_code")]
    public string Const_code { get; set; }
    [XmlElement(ElementName = "teneur")]
    public string Teneur { get; set; }
    [XmlElement(ElementName = "min")]
    public Min Min { get; set; }
    [XmlElement(ElementName = "max")]
    public Max Max { get; set; }
    [XmlElement(ElementName = "code_confiance")]
    public string Code_confiance { get; set; }
    [XmlElement(ElementName = "source_code")]
    public Source_code Source_code { get; set; }
}

[XmlRoot(ElementName = "TABLE")]
public class TABLE
{
    [XmlElement(ElementName = "COMPO")]
    public List<COMPO> COMPO { get; set; }
}

private void ReadCompoWithSerializer()
{
    string xmlFile = "test.xml";
    string xml = File.ReadAllText(xmlFile);
    XmlSerializer serializer = new XmlSerializer(typeof(TABLE));
    TABLE table = null;
    using (var reader = new StringReader(xml))
    {
        table = (TABLE)serializer.Deserialize(reader);
    }
}

Внутри объектов Min / Max / Source_code Value содержит значение, если оно присутствует (в этом случае Missing равно null), в противном случае Value равно null (в этом случае Missing содержит строку)


Рабочее решение на основе предложения jdweng

public class Compo  {
    public string alim_code { get; set; }
    public string const_code { get; set; }
    public string teneur { get; set; }
    public string min { get; set; }
    public string max { get; set; }
    public string code_confiance { get; set; }
    public string source_code { get; set; }
}

private void ReadCompoWithLinq() {
        const string FILENAME = "test.xml";
        XDocument doc = XDocument.Load(FILENAME);

        List<Compo> compos = doc.Descendants("COMPO").Select(x => new Compo()
        {
            alim_code = (string)x.Element("alim_code"),
            const_code = (string)x.Element("const_code"),
            teneur = (string)x.Element("teneur"),
            min = (x.Element("min").Attribute("missing") != null) ? null : (string)x.Element("min"),
            max = (x.Element("max").Attribute("missing") != null) ? null : (string)x.Element("max"),
            code_confiance = (string)x.Element("code_confiance"),
            source_code = (x.Element("source_code").Attribute("missing") != null) ? null : (string)x.Element("source_code"),
        }).ToList();
}

Объекты min, max, source_code содержат строковое значение, если оно предоставлено (в этом случае в описании XML используется атрибут "missing"), в противном случае строковое значение равно null.

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