Add values inside XML tags with C#

Asked

Viewed 68 times

1

I’m trying to create a C# script for a Plugin in Notepad++, where the goal is to add all the values inside the tags <ValorDesconto>, <ValorCancelamento> and <ValorTotalLiquido> of the XML file that is open and display the result.

For knowledge only, the Plugin inside Notepad++ is called Automation Scripts, follow:

inserir a descrição da imagem aqui

The values of tags ALWAYS are formatted with only two decimal places, so for a product value of R$21,347.10 (example), in XML will appear as 21347,10.

This is an example of XML:

<Produtos>
<Produto>
<Descricao>ESMALTE</Descricao>
<Quantidade>2,00</Quantidade>
<Unidade>UN</Unidade>
<ValorDesconto>2,00</ValorDesconto>
<ValorAcrescimo>0,00</ValorAcrescimo>
<ValorCancelamento>3,00</ValorCancelamento>
<ValorTotalLiquido>23,80</ValorTotalLiquido>
</Produto>

<Produto>
<Descricao>PINCEL</Descricao>
<Quantidade>5,00</Quantidade>
<Unidade>UN</Unidade>
<ValorDesconto>3,00</ValorDesconto>
<ValorAcrescimo>0,00</ValorAcrescimo>
<ValorCancelamento>8,00</ValorCancelamento>
<ValorTotalLiquido>32,10</ValorTotalLiquido>
</Produto>
</Produtos>

Inside the XML file the tag <Produto> (inside <Produtos>) repeats several times, one for each item, the same goes for the tag <Produtos> that repeats several times as well. In this example, the script would have to add the tags I mentioned and return the value of R$71.90.

Below is my code in C# that was made by a member here in the community but that is always returning the value "R$0" as you can see in the image below.

using System;
using System.Xml;
using System.Windows.Forms;
using NppScripts;

public class Script : NppScript
{
    public override void Run()
    {
        string path = Npp.Editor.GetCurrentFilePath();
        MessageBox.Show(path);

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(path);
       
        string xpath = "Produtos/Produto";
        XmlNodeList nodes = xmlDoc.SelectNodes(xpath);
        decimal values = 0;
        
        foreach(XmlNode childrenNode in nodes)
        {
            decimal valorDesconto = Decimal.Parse(childrenNode.SelectSingleNode(".//ValorDesconto").InnerText.Replace(',','.'));
            decimal valorCancelamento = Decimal.Parse(childrenNode.SelectSingleNode(".//ValorCancelamento").InnerText.Replace(',','.'));
            decimal valorLiquido =  Decimal.Parse(childrenNode.SelectSingleNode(".//ValorLiquido").InnerText.Replace(',','.'));
            values += valorDesconto + valorCancelamento + valorLiquido;
        }
        string result = "R$" + values.ToString().Replace('.',',');
        MessageBox.Show(result);
    }
}

inserir a descrição da imagem aqui

Where is the error in the code?

2 answers

2

A simpler way is to use the class XDocument, which allows you to use Linq in the xml, which makes operations much simpler.

Documentation: system.xml.Linq.xdocument

You can do this for example:

var somaValorDesconto = doc.Descendants("ValorDesconto")
            .Select(x => double.Parse(x.Value))
            .Sum();

Basically, selects all nodes "Valudiscount", and returns converted to double, and then sum.

Here however we have a problem, when converting the string (x.Value is the type string) for double we will have a format error as the Parse expects the number with dot and no comma in the decimals. This could be solved with a Replace to replace a semicolon by a period:

var somaValorDesconto = doc.Descendants("ValorDesconto")
            .Select(x => double.Parse(x.Value.Replace(",", ".")))
            .Sum();

Still, if any invalid value comes, the Parse can give error, so the best option is to use the TryParse, that if it is not possible to convert, no error:

var valorDesconto = doc.Descendants("ValorDesconto")
            .Select(x => double.TryParse(x.Value, NumberStyles.Float, culture, out var value) ? value : 0)
            .Sum();

To explain: double.TryParse(x.Value, NumberStyles.Float, culture, out var value) this returns true if it managed to convert and false if not. Also, if converted, the variable value will get the converted value, then the full expression will return value converted or 0 if a mistake has been made, and culture is the culture "pt-br", to treat the comma as a sign of decimals.

The code would look like this:

var culture = CultureInfo.GetCultureInfo("pt-BR");

var valorDesconto = doc.Descendants("ValorDesconto")
    .Select(x => double.TryParse(x.Value, NumberStyles.Float, culture, out var value) ? value : 0)
    .Sum();

var valorCancelamento = doc.Descendants("ValorCancelamento")
    .Select(x => double.TryParse(x.Value, NumberStyles.Float, culture, out var value) ? value : 0)
    .Sum();

var valorLiquido = doc.Descendants("ValorTotalLiquido")
    .Select(x => double.TryParse(x.Value, NumberStyles.Float, culture, out var value) ? value : 0)
    .Sum();

var valorTotal = valorDesconto + valorCancelamento + valorLiquido;

var result = string.Concat("R$ ",valorTotal.ToString("0.00", culture));

Can be seen working here: https://dotnetfiddle.net/kLZLqg

  • I replaced my code with yours, but here it didn’t work and returned error. What can it be? I put in Imgur: https://imgur.com/a/iY1TSZ6

0


I managed to solve, follow the C# code I used:

using System;
using System.Xml;
using System.Windows.Forms;
using NppScripts;

public class Script : NppScript
{
    public override void Run()
    {
        string path = Npp.Editor.GetCurrentFilePath();

        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(path);
       
        XmlNode root = xmlDoc.DocumentElement;
        XmlNodeList nodes = root.SelectNodes("Mensagem/Ecf/DadosReducaoZ/TotalizadoresParciais/TotalizadorParcial/ProdutosServicos/Produto");
        decimal values = 0;
        
        foreach (XmlNode childNode in nodes)
        {
          string desconto = childNode.SelectSingleNode("ValorDesconto").InnerText;
          string descontoReplaced = desconto.Replace(',', '.');
          decimal descontoParsed = Decimal.Parse(descontoReplaced);
          
          string cancelamento = childNode.SelectSingleNode("ValorCancelamento").InnerText;
          string cancelamentoReplaced = cancelamento.Replace(',', '.');
          decimal cancelamentoParsed = Decimal.Parse(cancelamentoReplaced);
          
          string totalliquido = childNode.SelectSingleNode("ValorTotalLiquido").InnerText;
          string totalliquidoReplaced = totalliquido.Replace(',', '.');
          decimal totalliquidoParsed = Decimal.Parse(totalliquidoReplaced);
          
          values += totalliquidoParsed + cancelamentoParsed + descontoParsed;
        }
        string result = "R$" + values.ToString().Replace('.',',');
        MessageBox.Show(result);
    }
}

Note that in the root.SelectNodes I had to add all Dad tags previous to tag <Produto>.

Browser other questions tagged

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