Проблемы сериализации в XML в атрибутах Ordering и XMLRoot - PullRequest
1 голос
/ 06 марта 2019

Я пишу класс для сериализации файла KiCAD BOM / Schematic. Класс работает нормально и корректно сериализует и десериализует исходный файл, но у меня две небольшие проблемы, которые я не могу исправить. Это код класса:

[XmlRoot(ElementName = "export")]
public class SchematicExport
{
    [XmlAttribute]
    public const string version = "D";

    public Design design { get; set; }

    [XmlArrayItem(typeof(Component), ElementName = "comp")]
    public List<Component> components;

    [XmlArrayItem(typeof(LibPart), ElementName = "libpart")]
    public List<LibPart> libparts;

    [XmlArrayItem(typeof(Library), ElementName = "library")]
    public List<Library> libraries;

    [XmlArrayItem(typeof(Net), ElementName = "net")]
    public List<Net> nets;

    public class Design
    {
        public string source { get; set; }

        public string date
        {
            get => _date.ToString("dd/MM/yyyy HH:mm:ss");
            set => _date = DateTime.Parse(value);
        }

        private DateTime _date;

        public string tool { get; set; }

        [XmlElement("sheet")]
        public List<Sheet> sheets { get; }

        public class Sheet
        {
            [XmlAttribute]
            public int number { get; set; }

            [XmlAttribute]
            public string name { get; set; }

            [XmlAttribute]
            public string tstamps { get; set; }

            public TitleBlock title_block { get; set; }

            public class TitleBlock
            {
                public string title { get; set; }

                public string company { get; set; }

                public string rev { get; set; }

                public string date
                {
                    get => _date.ToString("yyyy-MM-dd");
                    set => _date = DateTime.Parse(value);
                }

                private DateTime _date;

                public string source { get; set; }

                [XmlElement("comment")]
                public List<Comment> comments;

                public class Comment
                {
                    [XmlAttribute]
                    public int number;

                    [XmlAttribute]
                    public string value;

                    public Comment()
                    {
                    }

                    public Comment(int number, string value) : this()
                    {
                        this.number = number;
                        this.value = value;
                    }
                }

                public TitleBlock()
                {
                    comments = new List<Comment>();
                    _date = DateTime.Now;
                }

                public TitleBlock(string title, string company, string rev, string date, string source)
                {
                    this.title = title;
                    this.company = company;
                    this.rev = rev;
                    this.date = date;
                    this.source = source;
                }
            }

            public Sheet()
            {
                title_block = new TitleBlock();
                tstamps = DateTime.Now.ToFileTime().ToString("X8");
            }

            public Sheet(int number, string name) : this()
            {
                this.number = number;
                this.name = name;
            }
        }

        public Design()
        {
            sheets = new List<Sheet>();
            _date = DateTime.Now;
        }

        public Design(string source, string date, string tool) : this()
        {
            this.source = source;
            this.date = date;
            this.tool = tool;
        }
    }

    public class Component
    {
        [XmlAttribute("ref")]
        public string reference { get; set; }
        public string value { get; set; }
        public string footprint { get; set; }
        public string datasheet { get; set; }

        [XmlArrayItem(typeof(Field), ElementName = "field")]
        public List<Field> fields;

        public LibSource libsource { get; set; }
        public SheetPath sheetpath { get; set; }
        public string tstamp { get; set; }

        public class LibSource
        {
            [XmlAttribute]
            public string lib { get; set; }
            [XmlAttribute]
            public string part { get; set; }
            [XmlAttribute]
            public string description { get; set; }
        }

        public class SheetPath
        {
            [XmlAttribute]
            public string names { get; set; }
            [XmlAttribute]
            public string tstamps { get; set; }
        }

        public Component()
        {
            fields = new List<Field>();
            libsource = new LibSource();
            sheetpath = new SheetPath();

            tstamp = DateTime.Now.ToFileTime().ToString("X8");
        }

        public Component(string reference, string value, string footprint) : this()
        {
            this.reference = reference;
            this.value = value;
            this.footprint = footprint;
        }
    }

    public class LibPart
    {
        [XmlAttribute]
        public string lib { get; set; }

        [XmlAttribute]
        public string part { get; set; }

        [XmlArrayItem(typeof(Field), ElementName = "field")]
        public List<Field> fields;

        [XmlArrayItem(typeof(string), ElementName = "fp")]
        public List<string> footprints;

        [XmlArrayItem(typeof(Pin), ElementName = "pin")]
        public List<Pin> pins;

        public class Pin
        {
            [XmlAttribute]
            public string num { get; set; }

            [XmlAttribute]
            public string name { get; set; }

            [XmlAttribute]
            public string type { get; set; }

            public Pin()
            {
            }

            public Pin(string num, string name, string type) : this()
            {
                this.num = num;
                this.name = name;
                this.type = type;
            }
        }

        public LibPart()
        {
            fields = new List<Field>();
            footprints = new List<string>();
            pins = new List<Pin>();
        }

        public LibPart(string lib, string part) : this()
        {
            this.lib = lib;
            this.part = part;
        }
    }

    public class Library
    {
        [XmlAttribute]
        public string logical { get; set; }

        public string uri { get; set; }

        public Library() { }

        public Library(string logical, string uri) : this()
        {
            this.logical = logical;
            this.uri = uri;
        }
    }

    public class Net
    {
        [XmlAttribute]
        public string code { get; set; }

        [XmlAttribute]
        public string name { get; set; }

        [XmlElement("node")]
        public List<Node> nodes;

        public class Node
        {
            [XmlAttribute("ref")]
            public string reference { get; set; }

            [XmlAttribute]
            public string pin { get; set; }

            public Node() { }

            public Node(string reference, string pin) : this()
            {
                this.reference = reference;
                this.pin = pin;
            }
        }

        public Net()
        {
            nodes = new List<Node>();
        }

        public Net(string code, string name) : this()
        {
            this.code = code;
            this.name = name;
        }
    }

    public class Field
    {
        [XmlAttribute]
        public string name { get; set; }

        [XmlText]
        public string value { get; set; }

        public Field()
        {
        }

        public Field(string name, string value) : this()
        {
            this.name = name;
            this.value = value;
        }
    }

    public SchematicExport()
    {
        design = new Design();
        components = new List<Component>();
        libparts = new List<LibPart>();
        libraries = new List<Library>();
        nets = new List<Net>();
    }

    public void Serialze(string filename)
    {
        XmlSerializer _xmlSerializer = new XmlSerializer(typeof(SchematicExport));
        TextWriter _textWriter = new StreamWriter(filename);
        _xmlSerializer.Serialize(_textWriter, this);
    }

    public static SchematicExport Create(string filename)
    {
        if (!System.IO.File.Exists(filename))
            throw new Exception("File not existing");

        SchematicExport _schematicExport = null;

        XmlSerializer _serializer = new XmlSerializer(typeof(SchematicExport));

        StreamReader _reader = new StreamReader(filename);
        _schematicExport = (SchematicExport)_serializer.Deserialize(_reader);

        return _schematicExport;
    }
}

Первая проблема связана с элементом XmlRoot для SchematicExport. Я ожидал, что атрибут version появится внутри корневого тега XML, но это не так:

<export xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

Вторая проблема - порядок элементов (ОК, это не большая проблема, так как теги не зависят от порядка) Я попытался использовать атрибут XmlElement(Order=nn), чтобы вызвать его, но я только получил ряд Reflection исключений.

Мне только что удалось удалить xmlns:xsi и xmlns:xsd с незначительной модификацией в сериализаторе:

public void Serialze(string filename)
{
    XmlSerializer _xmlSerializer = new XmlSerializer(typeof(SchematicExport)); 
    XmlSerializerNamespaces _serializerNamespaces = new XmlSerializerNamespaces();

    _serializerNamespaces.Add("", "");

    using (TextWriter _textWriter = new StreamWriter(filename))
    {
        _xmlSerializer.Serialize(_textWriter, this, _serializerNamespaces);
    }
}

Я обнаружил еще одну (более вредоносную) проблему, даже если смогу десериализовать такой Xml:

?xml version="1.0" encoding="UTF-8"?>
<export version="D">
  <design>
    <source>C:\Users\m.santucci\Documents\Progetti\Viper\Schede Viper\V1-10A119\V1-10A119.sch</source>
    <date>07/03/2019 10:33:01</date>
    <tool>Eeschema (5.0.2)-1</tool>
    <sheet number="1" name="/" tstamps="/">
      <title_block>
        <title>V1-10A119</title>
        <company>ROB.INT. s.r.l.</company>
        <rev>01</rev>
        <date>2019-02-25</date>
        <source>V1-10A119.sch</source>
        <comment number="1" value="Michele Santucci"/>
        <comment number="2" value=""/>
        <comment number="3" value=""/>
        <comment number="4" value=""/>
      </title_block>
    </sheet>
  </design>
  <components>
    <comp ref="U5">
      <value>ADM3101E</value>
      <footprint>Analog:LFCSP-12_EP_3x3_Pitch0.5mm</footprint>
      <datasheet>https://www.analog.com/media/en/technical-documentation/data-sheets/adm3101e.pdf</datasheet>
      <fields>
        <field name="Code">820-9369 </field>
        <field name="P/N">ADM3101EACPZ-250R7 </field>
        <field name="Productor">Analog</field>
        <field name="Provider">RS</field>
        <field name="manf#">ADM3101EACPZ-250R7 </field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="ADM3101E-analog" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C83E717</tstamp>
    </comp>
    <comp ref="C35">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <datasheet>~</datasheet>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AA07</tstamp>
    </comp>
    <comp ref="C37">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <datasheet>~</datasheet>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AB0B</tstamp>
    </comp>
    <comp ref="C34">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <datasheet>~</datasheet>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AB65</tstamp>
    </comp>
    <comp ref="C38">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <datasheet>~</datasheet>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AC22</tstamp>
    </comp>
    <comp ref="C36">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <datasheet>~</datasheet>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84D1BA</tstamp>
    </comp>
  </components>
  <libparts>
    <libpart lib="conn" part="CONN_2">
      <description>Symbole general de connecteur</description>
      <fields>
        <field name="Reference">P</field>
        <field name="Value">CONN_2</field>
      </fields>
      <pins>
        <pin num="1" name="P1" type="passive"/>
        <pin num="2" name="PM" type="passive"/>
      </pins>
    </libpart>
  </libparts>
  <libraries>
    <library logical="V1-10A119-rescue">
      <uri>C:\Users\m.santucci\Documents\Progetti\Viper\Schede Viper\V1-10A119/V1-10A119-rescue.lib</uri>
    </library>
    <library logical="conn">
      <uri>C:/Users/Public/Documents/Kicad/library/conn.lib</uri>
    </library>
  </libraries>
  <nets>
    <net code="200" name="VBUS">
      <node ref="P9" pin="2"/>
      <node ref="U3" pin="3"/>
      <node ref="C12" pin="2"/>
      <node ref="C30" pin="2"/>
      <node ref="U4" pin="3"/>
    </net>
    <net code="201" name="Net-(LD13-Pad1)">
      <node ref="LD13" pin="1"/>
      <node ref="R37" pin="1"/>
    </net>
  </nets>
</export>

Полученная сериализация не завершена:

<?xml version="1.0" encoding="UTF-8"?>
<export version="D">
  <design>
    <source>C:\Users\m.santucci\Documents\Progetti\Viper\Schede Viper\V1-10A119\V1-10A119.sch</source>
    <date>07/03/2019 10:33:01</date>
    <tool>Eeschema (5.0.2)-1</tool>
    <sheet number="1" name="/" tstamps="/">
      <title_block>
        <title>V1-10A119</title>
        <company>ROB.INT. s.r.l.</company>
        <rev>01</rev>
        <date>2019-02-25</date>
        <source>V1-10A119.sch</source>
        <comment number="1" value="Michele Santucci"/>
        <comment number="2" value=""/>
        <comment number="3" value=""/>
        <comment number="4" value=""/>
      </title_block>
    </sheet>
  </design>
  <components>
    <comp ref="U5">
      <value>ADM3101E</value>
      <footprint>Analog:LFCSP-12_EP_3x3_Pitch0.5mm</footprint>
      <fields>
        <field name="Code">820-9369 </field>
        <field name="P/N">ADM3101EACPZ-250R7 </field>
        <field name="Productor">Analog</field>
        <field name="Provider">RS</field>
        <field name="manf#">ADM3101EACPZ-250R7 </field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="ADM3101E-analog" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C83E717</tstamp>
    </comp>
    <comp ref="C35">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AA07</tstamp>
    </comp>
    <comp ref="C37">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AB0B</tstamp>
    </comp>
    <comp ref="C34">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AB65</tstamp>
    </comp>
    <comp ref="C38">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84AC22</tstamp>
    </comp>
    <comp ref="C36">
      <value>100nF</value>
      <footprint>Capacitors_SMD:C_0603</footprint>
      <fields>
        <field name="Code">135-8345 </field>
        <field name="P/N">06035G104ZAT2A</field>
        <field name="Productor">AVX</field>
        <field name="Provider">RS</field>
        <field name="manf#">06035G104ZAT2A</field>
      </fields>
      <libsource lib="V1-10A119-rescue" part="C_Small-Device" description=""/>
      <sheetpath names="/MICRO/" tstamps="/5D2A20EE/"/>
      <tstamp>5C84D1BA</tstamp>
    </comp>
  </components>
  <libparts>
    <libpart lib="conn" part="CONN_2">
      <fields>
        <field name="Reference">P</field>
        <field name="Value">CONN_2</field>
      </fields>
      <pins>
        <pin num="1" name="P1" type="passive"/>
        <pin num="2" name="PM" type="passive"/>
      </pins>
    </libpart>
  </libparts>
  <libraries>
    <library logical="V1-10A119-rescue">
      <uri>C:\Users\m.santucci\Documents\Progetti\Viper\Schede Viper\V1-10A119/V1-10A119-rescue.lib</uri>
    </library>
    <library logical="conn">
      <uri>C:/Users/Public/Documents/Kicad/library/conn.lib</uri>
    </library>
  </libraries>
  <nets>
    <net code="200" name="VBUS">
      <node ref="P9" pin="2"/>
      <node ref="U3" pin="3"/>
      <node ref="C12" pin="2"/>
      <node ref="C30" pin="2"/>
      <node ref="U4" pin="3"/>
    </net>
    <net code="201" name="Net-(LD13-Pad1)">
      <node ref="LD13" pin="1"/>
      <node ref="R37" pin="1"/>
    </net>
  </nets>
</export>

Как видите, теги <datasheet> отсутствуют (секции comp), а также <description> (секции libpart).

Я не могу понять, в чем ошибка, но, видимо, проблема не в десериализации:

Debug session

1 Ответ

1 голос
/ 07 марта 2019

Первая проблема связана с элементом XmlRoot.Я ожидал, что атрибут version появится внутри тега XMLRoot, но его нет.

Как объяснено в Введение в сериализацию XML , XmlSerializerне будет сериализовать элемент const, даже если public:

Сериализация XML сериализует только открытые поля и значения свойств объекта в поток XML.XML-сериализация не включает информацию о типе.

Сериализация XML не преобразует методы, индексаторы, частные поля или свойства только для чтения (кроме коллекций только для чтения).Чтобы сериализовать все поля и свойства объекта, как публичные, так и приватные, используйте DataContractSerializer вместо XML-сериализации.

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

public const string version = "D";

[XmlAttribute("version")]
[System.ComponentModel.Browsable(false), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
public string Version { get { return version; } set { /* Do nothing */ } }

Свойство должно быть публичным, но вы можете уменьшить его видимость с помощью атрибутов Browsable, EditorBrowsable и DebuggerBrowsable.

Вторая проблема - это порядок элементов (хорошо, это не большая проблема, поскольку теги не зависят от порядка) Я попытался использовать атрибут XmlElement(Order=nn), чтобы вызвать его, но ятолько что получил ряд Reflection исключений.

Вы не предоставили пример этого, но я смог воспроизвести следующую проблему.Если я установлю XmlArrayAttribute.Order на некоторые , но не все членов коллекции SchematicExport, я получу следующее исключение:

System.InvalidOperationException: There was an error reflecting type 'SchematicExport'. 
---> System.InvalidOperationException: Inconsistent sequencing: if used on one of the class's members, the 'Order' property is required on all particle-like members, please explicitly set 'Order' using XmlElement, XmlAnyElement or XmlArray custom attribute on class member 'nets'.
   at System.Xml.Serialization.XmlReflectionImporter.InitializeStructMembers(StructMapping mapping, StructModel model, Boolean openModel, String typeName, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a, RecursionLimiter limiter)
   at System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel, RecursionLimiter limiter)

Демонстрационная скрипка # 1 здесь .

Решение состоит в том, чтобы последовать совету в сообщении об исключении и применить атрибут порядка ко всем сериализуемым членам в классе.

Таким образом, SchematicExport должен выглядеть примерно так:

[XmlRoot(ElementName = "export")]
public partial class SchematicExport
{
    public const string version = "D";

    [XmlAttribute("version")]
    [System.ComponentModel.Browsable(false), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never), System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
    public string Version { get { return version; } set { /* Do nothing */ } }

    [XmlElement(Order = 1)]
    public Design design { get; set; }

    [XmlArray(Order = 2)]
    [XmlArrayItem(typeof(Component), ElementName = "comp")]
    public List<Component> components;

    [XmlArray(Order = 3)]
    [XmlArrayItem(typeof(LibPart), ElementName = "libpart")]
    public List<LibPart> libparts;

    [XmlArray(Order = 4)]
    [XmlArrayItem(typeof(Library), ElementName = "library")]
    public List<Library> libraries;

    [XmlArray(Order = 5)]
    [XmlArrayItem(typeof(Net), ElementName = "net")]
    public List<Net> nets;
}

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

Кстати, в SchematicExport.Serialize(string) вам нужно закрыть StreamWriter.Самый простой способ сделать это с помощью оператора using:

public void Serialze(string filename)
{
    XmlSerializer _xmlSerializer = new XmlSerializer(typeof(SchematicExport));
    // FIXED ensure the file is closed.
    using (var _textWriter = new StreamWriter(filename))
    {
        _xmlSerializer.Serialize(_textWriter, this);
    }
}

Первая версия этого метода, показанная в вашем вопросе, этого не делает.

Демонстрационная скрипка № 2 здесь .

...