it-swarm.dev

Como desserializar o documento XML

Como faço para desserializar este documento XML:

<?xml version="1.0" encoding="utf-8"?>
<Cars>
  <Car>
    <StockNumber>1020</StockNumber>
    <Make>Nissan</Make>
    <Model>Sentra</Model>
  </Car>
  <Car>
    <StockNumber>1010</StockNumber>
    <Make>Toyota</Make>
    <Model>Corolla</Model>
  </Car>
  <Car>
    <StockNumber>1111</StockNumber>
    <Make>Honda</Make>
    <Model>Accord</Model>
  </Car>
</Cars>

Eu tenho isto:

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElementAttribute("StockNumber")]
    public string StockNumber{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Make")]
    public string Make{ get; set; }

    [System.Xml.Serialization.XmlElementAttribute("Model")]
    public string Model{ get; set; }
}

.

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }

}

.

public class CarSerializer
{
    public Cars Deserialize()
    {
        Cars[] cars = null;
        string path = HttpContext.Current.ApplicationInstance.Server.MapPath("~/App_Data/") + "cars.xml";

        XmlSerializer serializer = new XmlSerializer(typeof(Cars[]));

        StreamReader reader = new StreamReader(path);
        reader.ReadToEnd();
        cars = (Cars[])serializer.Deserialize(reader);
        reader.Close();

        return cars;
    }
}

que não parecem funcionar :-(

413
Alex

Aqui está uma versão de trabalho. Alterei os rótulos XmlElementAttribute para XmlElement porque no xml o StockNumber, os valores Make e Model são elementos, não atributos. Também removi o reader.ReadToEnd (); (isso function lê o fluxo inteiro e retorna uma string, então a função Deserialze () não pode mais usar o leitor ... a posição estava no final do fluxo). Eu também tomei algumas liberdades com o nome :).

Aqui estão as classes:

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElement("StockNumber")]
    public string StockNumber { get; set; }

    [System.Xml.Serialization.XmlElement("Make")]
    public string Make { get; set; }

    [System.Xml.Serialization.XmlElement("Model")]
    public string Model { get; set; }
}


[Serializable()]
[System.Xml.Serialization.XmlRoot("CarCollection")]
public class CarCollection
{
    [XmlArray("Cars")]
    [XmlArrayItem("Car", typeof(Car))]
    public Car[] Car { get; set; }
}

A função desserializar:

CarCollection cars = null;
string path = "cars.xml";

XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));

StreamReader reader = new StreamReader(path);
cars = (CarCollection)serializer.Deserialize(reader);
reader.Close();

E o xml ligeiramente ajustado (eu precisava adicionar um novo elemento para embrulhar <Cars> ... Net é exigente sobre a desserialização de matrizes):

<?xml version="1.0" encoding="utf-8"?>
<CarCollection>
<Cars>
  <Car>
    <StockNumber>1020</StockNumber>
    <Make>Nissan</Make>
    <Model>Sentra</Model>
  </Car>
  <Car>
    <StockNumber>1010</StockNumber>
    <Make>Toyota</Make>
    <Model>Corolla</Model>
  </Car>
  <Car>
    <StockNumber>1111</StockNumber>
    <Make>Honda</Make>
    <Model>Accord</Model>
  </Car>
</Cars>
</CarCollection>
321
Kevin Tighe

Que tal você apenas salvar o xml em um arquivo e usar xsd para gerar classes C #?

  1. Escreva o arquivo no disco (eu o chamei de foo.xml)
  2. Gere o xsd: xsd foo.xml
  3. Gere o C #: xsd foo.xsd /classes

Et voila - e arquivo de código C # que deve ser capaz de ler os dados via XmlSerializer:

    XmlSerializer ser = new XmlSerializer(typeof(Cars));
    Cars cars;
    using (XmlReader reader = XmlReader.Create(path))
    {
        cars = (Cars) ser.Deserialize(reader);
    }

(inclua os foo.cs gerados no projeto)

411
Marc Gravell

O snippet a seguir deve fazer o truque (e você pode ignorar a maioria dos atributos de serialização):

public class Car
{
  public string StockNumber { get; set; }
  public string Make { get; set; }
  public string Model { get; set; }
}

[XmlRootAttribute("Cars")]
public class CarCollection
{
  [XmlElement("Car")]
  public Car[] Cars { get; set; }
}

...

using (TextReader reader = new StreamReader(path))
{
  XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
  return (CarCollection) serializer.Deserialize(reader);
}
76
erymski

Veja se isso ajuda:

[Serializable()]
[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }
}

.

[Serializable()]
public class Car
{
    [System.Xml.Serialization.XmlElement()]
    public string StockNumber{ get; set; }

    [System.Xml.Serialization.XmlElement()]
    public string Make{ get; set; }

    [System.Xml.Serialization.XmlElement()]
    public string Model{ get; set; }
}

E, na falta disso, use o programa xsd.exe que vem com o visual studio para criar um documento de esquema baseado nesse arquivo xml e, em seguida, use-o novamente para criar uma classe baseada no documento do esquema.

22
Joel Coehoorn

Eu não acho que a .net seja 'exigente quanto à desserialização de matrizes'. O primeiro documento xml não está bem formado. Não há elemento raiz, embora pareça existir. O documento xml canônico tem uma raiz e pelo menos 1 elemento (se for o caso). No seu exemplo:

<Root> <-- well, the root
  <Cars> <-- an element (not a root), it being an array
    <Car> <-- an element, it being an array item
    ...
    </Car>
  </Cars>
</Root>
9
janbak

tente este bloco de código se seu arquivo .xml tiver sido gerado em algum lugar no disco e se você tiver usado List<T>:

//deserialization

XmlSerializer xmlser = new XmlSerializer(typeof(List<Item>));
StreamReader srdr = new StreamReader(@"C:\serialize.xml");
List<Item> p = (List<Item>)xmlser.Deserialize(srdr);
srdr.Close();`

Nota: C:\serialize.xml é o caminho do meu arquivo .xml. Você pode mudá-lo para as suas necessidades. 

7
sheetal nainwal

O anser de Kevin é bom, além do fato de que, no mundo real, muitas vezes você não consegue alterar o XML original para atender às suas necessidades.

Há uma solução simples para o XML original também:

[XmlRoot("Cars")]
public class XmlData
{
    [XmlElement("Car")]
    public List<Car> Cars{ get; set; }
}

public class Car
{
    public string StockNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

E então você pode simplesmente ligar:

var ser = new XmlSerializer(typeof(XmlData));
XmlData data = (XmlData)ser.Deserialize(XmlReader.Create(PathToCarsXml));
6
Kim Homann

Tente esta classe genérica para serialização e desserialização Xml. 

public class SerializeConfig<T> where T : class
{
    public static void Serialize(string path, T type)
    {
        var serializer = new XmlSerializer(type.GetType());
        using (var writer = new FileStream(path, FileMode.Create))
        {
            serializer.Serialize(writer, type);
        }
    }

    public static T DeSerialize(string path)
    {
        T type;
        var serializer = new XmlSerializer(typeof(T));
        using (var reader = XmlReader.Create(path))
        {
            type = serializer.Deserialize(reader) as T;
        }
        return type;
    }
}
4
Hasan Javaid

Para iniciantes

Eu encontrei as respostas aqui para ser muito útil, que disse que ainda me esforcei (apenas um pouco) para conseguir este trabalho. Então, caso isso ajude alguém, vou soletrar a solução de trabalho:

XML da pergunta original. O xml está em um arquivo Class1.xml, um path para este arquivo é usado no código para localizar esse arquivo xml.

Eu usei a resposta da @erymski para fazer isso funcionar, então criei um arquivo chamado Car.cs e acrescentei o seguinte:

using System.Xml.Serialization;  // Added

public class Car
{
    public string StockNumber { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
}

[XmlRootAttribute("Cars")]
public class CarCollection
{
    [XmlElement("Car")]
    public Car[] Cars { get; set; }
}

O outro bit de código fornecido pelo @erymski ...

using (TextReader reader = new StreamReader(path))
{
  XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
  return (CarCollection) serializer.Deserialize(reader);
}

... entra no seu programa principal (Program.cs), em static CarCollection XCar() assim:

using System;
using System.IO;
using System.Xml.Serialization;

namespace ConsoleApp2
{
    class Program
    {

        public static void Main()
        {
            var c = new CarCollection();

            c = XCar();

            foreach (var k in c.Cars)
            {
                Console.WriteLine(k.Make + " " + k.Model + " " + k.StockNumber);
            }
            c = null;
            Console.ReadLine();

        }
        static CarCollection XCar()
        {
            using (TextReader reader = new StreamReader(@"C:\Users\SlowLearner\source\repos\ConsoleApp2\ConsoleApp2\Class1.xml"))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(CarCollection));
                return (CarCollection)serializer.Deserialize(reader);
            }
        }
    }
}

Espero que ajude :-)

3
SlowLearner

A idéia é ter todos os níveis sendo tratados para a desserialização Por favor, veja uma amostra de solução que resolveu meu problema semelhante

<?xml version="1.0" ?> 
 <TRANSACTION_RESPONSE>
    <TRANSACTION>
        <TRANSACTION_ID>25429</TRANSACTION_ID> 
        <MERCHANT_ACC_NO>02700701354375000964</MERCHANT_ACC_NO> 
        <TXN_STATUS>F</TXN_STATUS> 
        <TXN_SIGNATURE>a16af68d4c3e2280e44bd7c2c23f2af6cb1f0e5a28c266ea741608e72b1a5e4224da5b975909cc43c53b6c0f7f1bbf0820269caa3e350dd1812484edc499b279</TXN_SIGNATURE> 
        <TXN_SIGNATURE2>B1684258EA112C8B5BA51F73CDA9864D1BB98E04F5A78B67A3E539BEF96CCF4D16CFF6B9E04818B50E855E0783BB075309D112CA596BDC49F9738C4BF3AA1FB4</TXN_SIGNATURE2> 
        <TRAN_DATE>29-09-2015 07:36:59</TRAN_DATE> 
        <MERCHANT_TRANID>150929093703RUDZMX4</MERCHANT_TRANID> 
        <RESPONSE_CODE>9967</RESPONSE_CODE> 
        <RESPONSE_DESC>Bank rejected transaction!</RESPONSE_DESC> 
        <CUSTOMER_ID>RUDZMX</CUSTOMER_ID> 
        <AUTH_ID /> 
        <AUTH_DATE /> 
        <CAPTURE_DATE /> 
        <SALES_DATE /> 
        <VOID_REV_DATE /> 
        <REFUND_DATE /> 
        <REFUND_AMOUNT>0.00</REFUND_AMOUNT> 
    </TRANSACTION>
  </TRANSACTION_RESPONSE> 

O XML acima é tratado em dois níveis 

  [XmlType("TRANSACTION_RESPONSE")]
public class TransactionResponse
{
    [XmlElement("TRANSACTION")]
    public BankQueryResponse Response { get; set; }

}

O nível interno 

public class BankQueryResponse
{
    [XmlElement("TRANSACTION_ID")]
    public string TransactionId { get; set; }

    [XmlElement("MERCHANT_ACC_NO")]
    public string MerchantAccNo { get; set; }

    [XmlElement("TXN_SIGNATURE")]
    public string TxnSignature { get; set; }

    [XmlElement("TRAN_DATE")]
    public DateTime TranDate { get; set; }

    [XmlElement("TXN_STATUS")]
    public string TxnStatus { get; set; }


    [XmlElement("REFUND_DATE")]
    public DateTime RefundDate { get; set; }

    [XmlElement("RESPONSE_CODE")]
    public string ResponseCode { get; set; }


    [XmlElement("RESPONSE_DESC")]
    public string ResponseDesc { get; set; }

    [XmlAttribute("MERCHANT_TRANID")]
    public string MerchantTranId { get; set; }

}

Same Way você precisa de vários níveis com car as arrayVerifique este exemplo para desserialização multinível

2
makdu

Você pode apenas alterar um atributo para a sua propriedade Carros do XmlArrayItem para o Elemento Xml. Isto é, desde

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlArrayItem(typeof(Car))]
    public Car[] Car { get; set; }
}

para 

[System.Xml.Serialization.XmlRootAttribute("Cars", Namespace = "", IsNullable = false)]
public class Cars
{
    [XmlElement("Car")]
    public Car[] Car { get; set; }
}
1
XU Weijiang
async public static Task<JObject> XMLtoNETAsync(XmlDocument ToConvert)
        {
            //Van XML naar JSON
            string jsonText = await Task.Run(() => JsonConvert.SerializeXmlNode(ToConvert));
            //Van JSON naar .net object
            var o = await Task.Run(() => JObject.Parse(jsonText));
            return o;
        }
1
Zoidbergseasharp

Minha solução:

  1. Use Edit > Past Special > Paste XML As Classes para obter a classe em seu código
  2. Tente algo assim: crie uma lista dessa classe (List<class1>) e use a XmlSerializer para serializar essa lista para um arquivo xml.
  3. Agora você apenas substitui o corpo desse arquivo com seus dados e tente deserialize ele.

Código: 

StreamReader sr = new StreamReader(@"C:\Users\duongngh\Desktop\Newfolder\abc.txt");
XmlSerializer xml = new XmlSerializer(typeof(Class1[]));
var a = xml.Deserialize(sr);
sr.Close();

NOTA: você deve prestar atenção ao nome da raiz, não alterá-lo. O meu é "ArrayOfClass1"

1
haiduong87

Se você estiver recebendo erros usando xsd.exe para criar seu arquivo xsd, use a classe XmlSchemaInference como mencionado no msdn . Aqui está um teste de unidade para demonstrar:

using System.Xml;
using System.Xml.Schema;

[TestMethod]
public void GenerateXsdFromXmlTest()
{
    string folder = @"C:\mydir\mydata\xmlToCSharp";
    XmlReader reader = XmlReader.Create(folder + "\some_xml.xml");
    XmlSchemaSet schemaSet = new XmlSchemaSet();
    XmlSchemaInference schema = new XmlSchemaInference();

    schemaSet = schema.InferSchema(reader);


    foreach (XmlSchema s in schemaSet.Schemas())
    {
        XmlWriter xsdFile = new XmlTextWriter(folder + "\some_xsd.xsd", System.Text.Encoding.UTF8);
        s.Write(xsdFile);
        xsdFile.Close();
    }
}

// now from the visual studio command line type: xsd some_xsd.xsd /classes
1
goku_da_master

Que tal uma classe genérica para desserializar um documento XML

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Generic class to load any xml into a class
// used like this ...
// YourClassTypeHere InfoList = LoadXMLFileIntoClass<YourClassTypeHere>(xmlFile);

using System.IO;
using System.Xml.Serialization;

public static T LoadXMLFileIntoClass<T>(string xmlFile)
{
    T returnThis;
    XmlSerializer serializer = new XmlSerializer(typeof(T));
    if (!FileAndIO.FileExists(xmlFile))
    {
        Console.WriteLine("FileDoesNotExistError {0}", xmlFile);
    }
    returnThis = (T)serializer.Deserialize(new StreamReader(xmlFile));
    return (T)returnThis;
}

Esta parte pode ou não ser necessária. Abra o documento XML no Visual Studio, clique com o botão direito no XML, escolha propriedades. Em seguida, escolha seu arquivo de esquema. 

0
David C Fuchs
string employeedata = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><tag><name>test</bar></nmae>";//demo xml data
        using (TextReader sr = new StringReader(employeedata))
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Employee));//pass type name in XmlSerializer constructor here
            Employee response = (Employee)serializer.Deserialize(sr);
            Console.WriteLine(response.name);
        }

[System.Xml.Serialization.XmlRoot("tag")]
public class Employee
{
    public string name { get; set; }
}
0
Sunil Dhappadhule