Xmltextreader() identify end of node and duplicate tags

Asked

Viewed 301 times

1

1 - How to identify equal tags with different nodes? ex: Categoria inside Dados and Formato

2 - How to identify who finished one product and started another?

I have XML (fictitious):

<produtos>
  <produto>
    <id>1</id>
    <dados>
       <categoria>teste</categoria>
    </dados>
    <formato>
    <categoria>teste2</categoria>
    </formato>
  </produto>
  <produto>
    <id>2</id>
     ...
  </produto>
</produtos>

IN THE C#

var produtos = new List<produto>();
var produto = new produto();
using (var reader = new XmlTextReader("aquivo.XML"))
{
    while (reader.Read())
    {
        if (reader.IsStartElement())
        {
            if (reader.Name == "ID")
            {
                produto.ID = reader.ReadString();
            }
            else if (reader.Name == "categoria") //?? 
            {
                produto.Dados.categoria = reader.ReadString();
            }
            else if (reader.Name == "categoria") //??
            {
                produto.Formato.categoria = reader.ReadString();
            }
        }
    //Final de produto
    produtos.Add(produto);
    }
}

He is reading the data correctly, but I don’t know how to identify the end of a product and understand in which tag, father he is.

OBS: I didn’t want to do it in Xmldocument or other format, because I already have enough of the code already working. change would only be in last case.

2 answers

2

This is the hard way to deserialize an XML of products.

It can be done as follows:

[XmlRoot("produtos")]
public class ListaProdutos
{
    [XmlElement("produto")]
    public List<Produto> Produtos { get; set; }
}

public class Produto
{
    [XmlElement("id")]
    public int Id { get; set; }
    [XmlElement("dados")]
    public Dados Dados { get; set; }
    [XmlElement("formato")]
    public Formato Formato { get; set; }
}

public class Dados
{
    [XmlElement("categoria")]
    public String Categoria { get; set; }
}

public class Formato
{
    [XmlElement("categoria")]
    public String Categoria { get; set; }
}

Use:

XmlSerializer serializer = new XmlSerializer(typeof(ListaProdutos));
using (TextReader reader = new StringReader(stringComXml))
{
    var resultado = (ListaProdutos) serializer.Deserialize(reader);
}
  • Um this may be a good tip, but what if XML is not standardized? ie some fields do not come filled or with a small change of structure? I would have an error or it would process?

  • The strategy is to formulate the class structure as comprehensively as possible. That is, suppose each node comes completely filled. The serializer will know what to do.

1


XML is a multilevel structure, and you’re trying to read "flat" form. What you are doing is basically the deserialization of XML for your C#objects, and the best way to do this is to use a serialization class (for example, Xmlserializer or Datacontractserializer).

Now, if you really want to do the job "at hand" (and I don’t advise, since the chance of you having errors in your implementation isn’t negligible) you need to store the context from where the nodes appear. One possibility is to store all the background nodes, and use this information, as in the example below:

class Program
{
    const string XML = @"<produtos>
                          <produto>
                            <id>1</id>
                            <dados>
                               <categoria>cat dados 1</categoria>
                            </dados>
                            <formato>
                            <categoria>cat formato 1</categoria>
                            </formato>
                          </produto>
                          <produto>
                            <id>2</id>
                            <dados>
                               <categoria>cat dados 2</categoria>
                            </dados>
                            <formato>
                            <categoria>cat formato 2</categoria>
                            </formato>
                          </produto>
                        </produtos>";

    static void Main()
    {
        Stack<string> pilha = new Stack<string>();
        var produtos = new List<Produto>();
        var produto = new Produto { Dados = new Dados(), Formato = new Formato() };
        using (var reader = new XmlTextReader(new StringReader(XML)))
        {
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    if (reader.Name == "ID")
                    {
                        produto.ID = reader.ReadString();
                    }
                    else if (reader.Name == "categoria")
                    {
                        var topoDaPilha = pilha.Peek();
                        if (topoDaPilha == "dados")
                        {
                            produto.Dados.Categoria = reader.ReadString();
                        }
                        else if (topoDaPilha == "formato")
                        {
                            produto.Formato.Categoria = reader.ReadString();
                        }
                    }
                    else if (!reader.IsEmptyElement)
                    {
                        pilha.Push(reader.Name);
                    }
                }
                else if (reader.NodeType == XmlNodeType.EndElement)
                {
                    pilha.Pop();
                    if (reader.Name == "produto")
                    {
                        produtos.Add(produto);
                        produto = new Produto { Dados = new Dados(), Formato = new Formato() };
                    }
                }
            }
        }
        Console.WriteLine("Produtos:");
        foreach (var prod in produtos)
        {
            Console.WriteLine("- {0}", prod);
        }
    }
}

class Produto
{
    public string ID { get; set; }
    public Dados Dados { get; set; }
    public Formato Formato { get; set; }

    public override string ToString()
    {
        return string.Format("Produto[ID={0}, Dados/Cat={1}, Formato/Cat={2}]", ID, Dados.Categoria, Formato.Categoria);
    }
}
class Dados
{
    public string Categoria { get; set; }
}
class Formato
{
    public string Categoria { get; set; }
}

Another possibility is to accept the recursiveness of XML, and use functions to read each part of your document:

class Program
{
    const string XML = @"<produtos>
                          <produto>
                            <id>1</id>
                            <dados>
                               <categoria>cat dados 1</categoria>
                            </dados>
                            <formato>
                            <categoria>cat formato 1</categoria>
                            </formato>
                          </produto>
                          <produto>
                            <id>2</id>
                            <dados>
                               <categoria>cat dados 2</categoria>
                            </dados>
                            <formato>
                            <categoria>cat formato 2</categoria>
                            </formato>
                          </produto>
                        </produtos>";

    static void Main(string[] args)
    {
        var produtos = new List<Produto>();
        using (var reader = new XmlTextReader(new StringReader(XML)))
        {
            reader.ReadToDescendant("produto");
            while (reader.IsStartElement("produto"))
            {
                produtos.Add(ReadProduto(reader));
            }
        }

        Console.WriteLine("Produtos:");
        foreach (var produto in produtos)
        {
            Console.WriteLine("- {0}", produto);
        }
    }

    static Produto ReadProduto(XmlReader reader)
    {
        Debug.Assert(reader.LocalName == "produto");
        Produto produto = new Produto();
        if (reader.IsEmptyElement)
        {
            // Vazio, não tem filhos. Avança pro próximo nó e retorna
            ReadToNextElementOrEndElement(reader);
            return produto;
        }

        ReadToNextElementOrEndElement(reader);

        do
        {
            if (reader.NodeType == XmlNodeType.Element)
            {
                if (reader.Name == "id")
                {
                    produto.ID = reader.ReadString();
                    if (reader.NodeType == XmlNodeType.EndElement) ReadToNextElementOrEndElement(reader);
                }
                else if (reader.Name == "dados")
                {
                    produto.Dados = ReadDados(reader);
                }
                else if (reader.Name == "formato")
                {
                    produto.Formato = ReadFormato(reader);
                }
                else
                {
                    throw new ArgumentException("No do XML nao reconhecido");
                }
            }
            else
            {
                ReadToNextElementOrEndElement(reader);
            }
        } while (reader.NodeType != XmlNodeType.EndElement);

        Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
        ReadToNextElementOrEndElement(reader); // avança pro próximo produto ou fim

        return produto;
    }

    static Dados ReadDados(XmlReader reader)
    {
        Debug.Assert(reader.NodeType == XmlNodeType.Element);
        Debug.Assert(reader.LocalName == "dados");
        Dados dados = new Dados();
        if (reader.IsEmptyElement)
        {
            // Vazio, não tem filhos. Avança pro próximo nó e retorna
            ReadToNextElementOrEndElement(reader);
            return dados;
        }

        do
        {
            ReadToNextElementOrEndElement(reader);
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "categoria")
            {
                dados.Categoria = reader.ReadString();
                if (reader.NodeType == XmlNodeType.EndElement) reader.Read();
            }
        } while (reader.NodeType != XmlNodeType.EndElement || reader.Name != "dados");

        Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
        ReadToNextElementOrEndElement(reader); // avança pro próximo produto ou fim

        return dados;
    }

    static Formato ReadFormato(XmlReader reader)
    {
        Debug.Assert(reader.NodeType == XmlNodeType.Element);
        Debug.Assert(reader.LocalName == "formato");
        Formato formato = new Formato();
        if (reader.IsEmptyElement)
        {
            // Vazio, não tem filhos. Avança pro próximo nó e retorna
            ReadToNextElementOrEndElement(reader);
            return formato;
        }

        do
        {
            ReadToNextElementOrEndElement(reader);
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "categoria")
            {
                formato.Categoria = reader.ReadString();
                if (reader.NodeType == XmlNodeType.EndElement) reader.Read();
            }
        } while (reader.NodeType != XmlNodeType.EndElement || reader.Name != "formato");

        Debug.Assert(reader.NodeType == XmlNodeType.EndElement);
        ReadToNextElementOrEndElement(reader); // avança pro próximo produto ou fim

        return formato;
    }

    static bool ReadToNextElementOrEndElement(XmlReader reader)
    {
        do
        {
            if (!reader.Read()) return false;
        } while (reader.NodeType != XmlNodeType.Element && reader.NodeType != XmlNodeType.EndElement);
        return true;
    }
}

class Produto
{
    public string ID { get; set; }
    public Dados Dados { get; set; }
    public Formato Formato { get; set; }

    public override string ToString()
    {
        return string.Format("Produto[ID={0}, Dados/Cat={1}, Formato/Cat={2}]", ID, Dados.Categoria, Formato.Categoria);
    }
}
class Dados
{
    public string Categoria { get; set; }
}
class Formato
{
    public string Categoria { get; set; }
}
  • I liked the first way more and really you’re right I already imagined that using Xmltoreader the chance to give a reading problem is great, since I practically read it as a text. I will study about Xmlserializer to see if it helps in this issue. However, my code is already getting too complex. rs

Browser other questions tagged

You are not signed in. Login or sign up in order to post.