CodeGym /Corsi /C# SELF /Configurazione della serializzazione XML

Configurazione della serializzazione XML

C# SELF
Livello 48, Lezione 1
Disponibile

1. Introduzione

Sai già che .NET può serializzare e deserializzare oggetti in XML di default, basandosi su campi e proprietà pubbliche. Ma quando vuoi ottenere un XML ordinato, compatibile con altri programmi/standard, o quando non vuoi includere dettagli interni, serve una configurazione più precisa.

In C# per questo ci sono gli attributi di serializzazione — delle "etichette" che applichiamo a classi, proprietà o campi. Grazie a questi attributi possiamo controllare praticamente tutto: quali campi finiscono nell'XML, quali vengono ignorati, come si chiama un elemento, se diventa un attributo, avere strutture complesse e persino l'ordine degli elementi!

Immagina la serializzazione di default come il completamento automatico di un modulo: nome-cognome-secondoNome nelle sue caselle, tutto il resto dove capita. Con gli attributi sei come un funzionario esperto: decidi tu dove va ogni riga, cosa saltare, cosa evidenziare e cosa mettere nella sezione segreta.

Mappatura tra oggetto e XML

Classe/Proprietà Attributo Risultato in XML
[XmlRoot("Persons")]
Classe UserList
<Persons>...</Persons>
List<User> Users
[XmlElement("Person")]
<Person>...</Person>
int Id
[XmlAttribute("id")]
id="..." (attributo)
string Name
[XmlElement("FullName")]
<FullName>...</FullName>
string[] Emails
[XmlArray("EMails")][XmlArrayItem("Email")]
<EMails><Email>...</Email></EMails>
DateTime RegisteredAt
[XmlIgnore] (vedi RegisteredAtString) -
string RegisteredAtString
[XmlAttribute("registered")]
registered="..."
Address[] Addresses
[XmlArray("UserAddresses")][XmlArrayItem("Address")]
<UserAddresses>...</UserAddresses>

2. Panoramica dei principali attributi di serializzazione XML

Passiamo in rassegna gli attributi più popolari che gli sviluppatori .NET usano da anni per decorare le loro classi!

Attributo [XmlElement] — Nome dell'elemento

Cambia il nome dell'elemento in XML o marca la proprietà/campo come elemento XML.

public class Person
{
    [XmlElement("FullName")]
    public string Name { get; set; }
}

XML:

<Person>
  <FullName>Vasiliy Petrov</FullName>
</Person>

Se non specifichi l'attributo — per default il nome dell'elemento corrisponderà al nome della proprietà/campo.

Attributo [XmlAttribute] — Serializzazione come attributo XML

Permette di serializzare una proprietà/campo come attributo XML, non come elemento.

public class Person
{
    [XmlAttribute("id")]
    public int Id { get; set; }
}

XML:

<Person id="123"></Person>

Nei reali API XML spesso si usano attributi per identificatori, date, flag, ecc.

Attributo [XmlIgnore] — Escludere campo/proprietà

Il sogno di ogni paranoico: escludere completamente una proprietà dalla serializzazione e deserializzazione! Tutto ciò che è marcato con questo attributo semplicemente non finirà nell'XML finale.

public class Person
{
    [XmlIgnore]
    public string InternalNote { get; set; }
}

XML: (la proprietà InternalNote non esiste qui e non esisterà)

Attributi [XmlArray] e [XmlArrayItem] — controllo sulle collezioni

Attributi speciali per array e collezioni. [XmlArray] imposta il nome del wrapper esterno, mentre [XmlArrayItem] imposta il nome di ogni elemento.

public class Person
{
    [XmlArray("Phones")]
    [XmlArrayItem("Phone")]
    public string[] PhoneNumbers { get; set; }
}

XML:

<Person>
  <Phones>
    <Phone>+12951234567</Phone>
    <Phone>+12876543210</Phone>
  </Phones>
</Person>

Attributo [XmlRoot] — Nome dell'elemento radice

Marca la classe per cambiare il nome dell'elemento radice nell'XML.

[XmlRoot("User")]
public class Person
{
    public string Name { get; set; }
}

XML:

<User>
  <Name>Anna</Name>
</User>

Attributo [XmlText] — Serializzazione nel contenuto testuale dell'elemento

A volte (o per specifiche) vuoi che il valore di una proprietà sia semplicemente il testo interno dell'elemento, non un sottoelemento o un attributo.

public class Note
{
    [XmlText]
    public string Content { get; set; }
}

XML:

<Note>Chiamare la nonna!</Note>

Attributi [XmlNamespaceDeclarations], [XmlElement(Type = ...)]

Per scenari più avanzati (es. serializzazione con namespace, supporto dell'ereditarietà, ecc.) ci sono anche attributi più specifici. Dai un'occhiata alla documentazione ufficiale sulla serializzazione XML per l'elenco completo.

3. Pratica: miglioriamo la serializzazione con un esempio reale

Passo dopo passo, miglioriamo la nostra app didattica fino a ottenere una "serializzazione XML di livello premium" che piaccia anche all'ispettore XML più severo.

Classe base senza attributi

Supponiamo di avere questa classe utente:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string[] Emails { get; set; }
    public DateTime RegisteredAt { get; set; }
    public string Password { get; set; }
}

La serializzazione di default produrrà un XML simile (e la data sarà nel suo formato):

<User>
  <Id>42</Id>
  <Name>Ivan Ivanov</Name>
  <Emails>
    <string>user@mail.com</string>
    <string>admin@site.org</string>
  </Emails>
  <RegisteredAt>2024-07-01T00:00:00</RegisteredAt>
  <Password>qwerty</Password>
</User>

Cosa non va:

  • Meglio non serializzare affatto la password.
  • Vogliamo la data come attributo in un formato leggibile.
  • Vogliamo gli Emails in una forma più pulita.
  • Vogliamo l'elemento radice <Person>.

Aggiungiamo le configurazioni tramite attributi

[XmlRoot("Person")]
public class User
{
    [XmlAttribute("id")]
    public int Id { get; set; }

    [XmlElement("FullName")]
    public string Name { get; set; }

    [XmlArray("EMails")]
    [XmlArrayItem("Email")]
    public string[] Emails { get; set; }

    [XmlAttribute("registered")]
    public string RegisteredAtString
    {
        get => RegisteredAt.ToString("yyyy-MM-dd");
        set => RegisteredAt = DateTime.Parse(value);
    }

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

    [XmlIgnore]
    public DateTime RegisteredAt { get; set; }
}

Spiegazione dei dettagli:

  • Usiamo [XmlAttribute("registered")] per serializzare la data come attributo.
  • Per serializzare non il tipo DateTime ma la sua rappresentazione testuale, aggiungiamo una proprietà separata RegisteredAtString, mentre il campo reale lo nascondiamo con [XmlIgnore].
  • Nascondiamo la password con [XmlIgnore].
  • L'array di email è configurato in modo che il tag esterno sia <EMails> e ogni email sia <Email>.

Risultato:

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
    <Email>admin@site.org</Email>
  </EMails>
</Person>

4. Lavorare con oggetti annidati

XML rende al meglio quando ci sono oggetti annidati (es. utente e i suoi indirizzi).

public class Address
{
    [XmlAttribute]
    public string City { get; set; }

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

public class User
{
    // ...altre proprietà, vedi sopra...

    public Address[] Addresses { get; set; }
}

Aggiungiamo la configurazione per l'array di indirizzi:

[XmlArray("UserAddresses")]
[XmlArrayItem("Address")]
public Address[] Addresses { get; set; }

Allora l'XML sarà così:

<Person id="42" registered="2024-07-01">
  <FullName>Ivan Ivanov</FullName>
  <EMails>
    <Email>user@mail.com</Email>
  </EMails>
  <UserAddresses>
    <Address City="Neon city">ul. Zamkovaja, d.1</Address>
    <Address City="North Cave">ul. Krajnyaja d.12</Address>
  </UserAddresses>
</Person>

Si vede che per ogni <Address> city è un attributo, mentre l'indirizzo vero è il testo interno.

5. Errori comuni e dettagli

Se serializzi un oggetto complesso e non vedi i cambiamenti nell'XML che ti aspettavi — probabilmente hai dimenticato di applicare correttamente un attributo. A volte le proprietà/campi restano senza marca; .NET quindi userà il comportamento di default.

Un'altra trappola è il nome della collezione. Se la collezione (es. array di stringhe) non è marcata né con [XmlArray] né con [XmlArrayItem], gli elementi verranno scritti con il tag string (come nell'esempio iniziale). Per evitare questo, imposta sempre esplicitamente i nomi dei tag con gli attributi.

Stai attento con le proprietà "getter-only" (solo lettura) — queste non vengono deserializzate di default. Cerca di avere un set pubblico quando possibile.

Alcuni tipi (per esempio, dizionari o interfacce) non vengono serializzati dal XmlSerializer standard, o richiedono un trattamento speciale.

2
Compito
C# SELF, livello 48, lezione 1
Bloccato
Serializzazione di una collezione usando XmlArray e XmlArrayItem
Serializzazione di una collezione usando XmlArray e XmlArrayItem
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION