CodeGym /Corsi /C# SELF /Lettura e scrittura XML: X...

Lettura e scrittura XML: XmlSerializer

C# SELF
Livello 48 , Lezione 0
Disponibile

1. Introduzione

XML (eXtensible Markup Language) — un linguaggio di markup estensibile, un formato diffuso per memorizzare e scambiare dati strutturati complessi. A differenza di JSON, XML non è particolarmente conciso, ma è ancora molto usato nel settore bancario, nello scambio tra servizi pubblici, nei documenti e nelle integrazioni con 1С, SAP, Oracle ecc. Alcuni dati in Windows e .NET (per esempio i config) sono ancora conservati in formato XML.

Esteticamente XML assomiglia a HTML, solo che non ha un set fisso di tag come <body> o <div> — definisci tu la struttura degli elementi. Esempio:

<Person>
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</Person>

Basi di XML e mapping agli oggetti C#

A differenza di JSON, in XML non ci sono tipi speciali per array o booleani — tutto è rappresentato da elementi e attributi. Le strutture a lista si realizzano con elementi ripetuti che hanno lo stesso nome.

Per lavorare comodamente con XML in C# si usa il meccanismo di serializzazione/deserializzazione tramite XmlSerializer.

Serializzazione — la conversione di un oggetto C# in una stringa XML. Deserializzazione — il processo inverso: ottenere un oggetto C# da XML.

Funziona "out of the box" se le tue classi rispettano certe condizioni — le vedremo sotto.

Perché usare XmlSerializer?

XmlSerializer — lo strumento standard di .NET (namespace System.Xml.Serialization) per convertire oggetti in XML e viceversa. È semplice, type-safe e adatto per import/export di dati, config, scambi tra sistemi e integrazioni con servizi pubblici.

2. Esempio: serializzare un oggetto

Supponiamo di avere il modello utente:

public class User
{
    public string FirstName { get; set; }
    public string LastName  { get; set; }
    public int Age          { get; set; }
}

Serializziamo un oggetto in XML:

using System.Xml.Serialization;

User user = new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 };
// Per la serializzazione serve il tipo
XmlSerializer serializer = new XmlSerializer(typeof(User));

// Scriviamo l'XML su file
using FileStream fs = new FileStream("user.xml", FileMode.Create);
serializer.Serialize(fs, user);

Che succede?

- Crei un'istanza della classe User.
- Crei un'istanza di XmlSerializer per il tipo User.
- Apri il file con FileStream.
- Chiami il metodo Serialize(), che trasforma l'oggetto in XML e lo scrive sul file.

Contenuto del file user.xml:

<?xml version="1.0"?>
<User xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <FirstName>Ivan</FirstName>
  <LastName>Petrov</LastName>
  <Age>30</Age>
</User>

Vedi? Poco codice e l'XML è pronto.

3. Deserializzazione: leggere l'XML e ricreare l'oggetto

Leggiamo l'XML creato e ricostruiamo l'oggetto C#:

// Apriamo il file XML
using FileStream fs = new FileStream("user.xml", FileMode.Open);
User? loadedUser = serializer.Deserialize(fs) as User;
Console.WriteLine($"{loadedUser?.FirstName} {loadedUser?.LastName}, età: {loadedUser?.Age}");

Il metodo Deserialize() restituisce un object, quindi facciamo il cast a User (con as). Se il file è corretto e corrisponde al modello, l'oggetto viene ricostruito completamente.

4. Come funziona la serializzazione: caratteristiche tipiche

La serializzazione in XML richiede il rispetto di certe regole:

• La classe da serializzare deve avere un costruttore pubblico di default (public senza parametri).

• Le proprietà serializzabili devono essere pubbliche: servono sia getter che setter. I membri privati vengono ignorati.

• Non funzionano le proprietà read-only, né le proprietà statiche o astratte.

Se violi le regole riceverai una InvalidOperationException con un messaggio tipo "XmlSerializer cannot serialize this type".

5. Serializzazione di collezioni: array e liste

Se devi serializzare più utenti, usa una classe wrapper:

public class UserList
{
    public List<User> Users { get; set; } = new List<User>();
}

Creiamo la collezione e serializziamo:

UserList users = new UserList
{
    Users = new List<User>
    {
        new User { FirstName = "Ivan", LastName = "Petrov", Age = 30 },
        new User { FirstName = "Maria", LastName = "Ivanova", Age = 25 }
    }
};

XmlSerializer serializer = new XmlSerializer(typeof(UserList));
using FileStream fs = new FileStream("users.xml", FileMode.Create);
serializer.Serialize(fs, users);

Otteniamo l'XML:

<?xml version="1.0"?>
<UserList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Users>
    <User>
      <FirstName>Ivan</FirstName>
      <LastName>Petrov</LastName>
      <Age>30</Age>
    </User>
    <User>
      <FirstName>Maria</FirstName>
      <LastName>Ivanova</LastName>
      <Age>25</Age>
    </User>
  </Users>
</UserList>

Si può serializzare semplicemente List<User>? Sì, ma l'elemento radice si chiamerà ArrayOfUser, cosa che non sempre va bene per le integrazioni.

6. Attributi per controllare la serializzazione

Con gli attributi puoi controllare il formato XML: rinominare elementi, serializzare come attributi, ignorare campi, ecc. Attributi utili: [XmlElement], [XmlAttribute], [XmlArray], [XmlIgnore].

Serializzare come attributi

Scriviamo nome e cognome come attributi e l'età come elemento:

public class User
{
    [XmlAttribute]
    public string FirstName { get; set; }

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

    [XmlElement]
    public int Age { get; set; }
}

Risultato:

<User FirstName="Ivan" LastName="Petrov">
  <Age>30</Age>
</User>

Controllo dei nomi degli elementi

Rinominiamo i tag in modo più leggibile:

public class User
{
    [XmlElement("Nome")]
    public string FirstName { get; set; }

    [XmlElement("Cognome")]
    public string LastName { get; set; }

    [XmlElement("Età")]
    public int Age { get; set; }
}

XML risultante:

<User>
  <Nome>Ivan</Nome>
  <Cognome>Petrov</Cognome>
  <Età>30</Età>
</User>

Ignorare proprietà

Se una proprietà non deve essere serializzata, marcala con [XmlIgnore]:

public class User
{
    public string FirstName { get; set; }

    [XmlIgnore]
    public string InternalCode { get; set; }
}

7. Serializzazione di gerarchie e oggetti annidati

Le classi annidate si trasformano facilmente in elementi XML annidati:

public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

public class User
{
    public string FirstName { get; set; }
    public Address Address { get; set; }
}
var user = new User
{
    FirstName = "Anna",
    Address = new Address { City = "Neongrad", Street = "Vyazovaya" }
};

XmlSerializer serializer = new XmlSerializer(typeof(User));
using var fs = new FileStream("user_with_address.xml", FileMode.Create);
serializer.Serialize(fs, user);

XML:

<User>
  <FirstName>Anna</FirstName>
  <Address>
    <City>Neongrad</City>
    <Street>Vyazovaya</Street>
  </Address>
</User>

8. Serializzazione da/a stringa (senza file)

Serializzare in una stringa

using System.Text;

var serializer = new XmlSerializer(typeof(User));
using var stringWriter = new StringWriter();

serializer.Serialize(stringWriter, user);
string xml = stringWriter.ToString();
Console.WriteLine(xml);

Deserializzare da una stringa

var xml = "<User><FirstName>Anna</FirstName></User>";
using var stringReader = new StringReader(xml);
User user = (User)serializer.Deserialize(stringReader);
Console.WriteLine(user.FirstName);

9. Esempio: export e import dei dati dell'app

Export/import della lista utenti su file XML:

public static void ExportUsers(UserList users, string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Create);
    serializer.Serialize(fs, users);
}

public static UserList ImportUsers(string path)
{
    var serializer = new XmlSerializer(typeof(UserList));
    using var fs = new FileStream(path, FileMode.Open);
    return (UserList)serializer.Deserialize(fs);
}

Uso: backup, scambio con altri servizi, integrazioni. Ai colloqui le domande su XmlSerializer capitano più spesso di quanto pensi — specialmente in aziende che lavorano con servizi pubblici e banche.

10. Errori tipici e caratteristiche del funzionamento

Se un campo non è public, non viene serializzato.

Le proprietà read-only vengono ignorate.

Proprietà di tipo interfaccia o di classe astratta non sono gestite dal serializer.

Il tipo DateTime viene serializzato nel formato XSD (vedi la documentazione).

Collezioni come array e List<T> funzionano in modo prevedibile, mentre con ObservableCollection<T> possono esserci sorprese per sistemi esterni.

Dettagli sulle codifiche: se scrivi con StreamWriter senza specificare la codifica, potresti ottenere UTF-16; per compatibilità spesso ci si aspetta UTF-8 — imposta la codifica esplicitamente.

2
Compito
C# SELF, livello 48, lezione 0
Bloccato
Serializzazione di una lista di oggetti
Serializzazione di una lista di oggetti
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION