0

I am having a bad time trying to consume an XML service.

This is my code to consume the service (it is a public service so you can consume it):

using( var client = new HttpClient() )
{
    client.DefaultRequestHeaders
        .Accept
        .Add( new MediaTypeWithQualityHeaderValue( "text/xml" ) );
    var request = new HttpRequestMessage( HttpMethod.Get, "https://gee.bccr.fi.cr/Indicadores/Suscripciones/WS/wsindicadoreseconomicos.asmx/ObtenerIndicadoresEconomicosXML?Indicador=317&FechaInicio=20%2F04%2F2022&FechaFinal=20%2F04%2F2022&Nombre=Jos%C3%A9+Miguel+Torres+G%C3%B3mez&SubNiveles=N&CorreoElectronico=josetorres%40outlook.com&Token=2LOEU2EM8O" );
    var returnedXml = client.SendAsync( request ).Result.Content.ReadAsStringAsync().Result;

    Console.WriteLine( returnedXml );
}

The response I get from there is the following, adding the console screenshot due to it returns special scape chars: enter image description here

<string xmlns="http://ws.sdde.bccr.fi.cr">
  <Datos_de_INGC011_CAT_INDICADORECONOMIC>
    <INGC011_CAT_INDICADORECONOMIC>
      <COD_INDICADORINTERNO>317</COD_INDICADORINTERNO>
      <DES_FECHA>2022-04-20T00:00:00-06:00</DES_FECHA>
      <NUM_VALOR>650.10000000</NUM_VALOR>
    </INGC011_CAT_INDICADORECONOMIC>
  </Datos_de_INGC011_CAT_INDICADORECONOMIC>
</string>

This is the class I use to try to deserialize:

public class Datos_de_INGC011_CAT_INDICADORECONOMIC
{
    public INGC011_CAT_INDICADORECONOMIC INGC011_CAT_INDICADORECONOMIC { get; set; }
}
public class INGC011_CAT_INDICADORECONOMIC
{
    public int COD_INDICADORINTERNO { get; set; }
    public DateTime DES_FECHA { get; set; }
    public double NUM_VALOR { get; set; }
}

But when I try to Deserialize the string I get the error: There is an error in XML document. Data at the root level is invalid. And due to I don't use to work with XML data I am stocked here.

jtorrescr
  • 627
  • 4
  • 13

2 Answers2

1

Looks like the problem you are having is because of the outer element you have in your xml (string)

You can use one of the ways mentioned here (https://stackoverflow.com/a/19613934/3377344) in this post to create your class.

I used the paste special method shown there and here is the class I was able to generate:

// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://ws.sdde.bccr.fi.cr")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://ws.sdde.bccr.fi.cr", IsNullable = false)]
public partial class @string
{

    private stringDatos_de_INGC011_CAT_INDICADORECONOMIC datos_de_INGC011_CAT_INDICADORECONOMICField;

    /// <remarks/>
    public stringDatos_de_INGC011_CAT_INDICADORECONOMIC Datos_de_INGC011_CAT_INDICADORECONOMIC
    {
        get
        {
            return this.datos_de_INGC011_CAT_INDICADORECONOMICField;
        }
        set
        {
            this.datos_de_INGC011_CAT_INDICADORECONOMICField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://ws.sdde.bccr.fi.cr")]
public partial class stringDatos_de_INGC011_CAT_INDICADORECONOMIC
{

    private stringDatos_de_INGC011_CAT_INDICADORECONOMICINGC011_CAT_INDICADORECONOMIC iNGC011_CAT_INDICADORECONOMICField;

    /// <remarks/>
    public stringDatos_de_INGC011_CAT_INDICADORECONOMICINGC011_CAT_INDICADORECONOMIC INGC011_CAT_INDICADORECONOMIC
    {
        get
        {
            return this.iNGC011_CAT_INDICADORECONOMICField;
        }
        set
        {
            this.iNGC011_CAT_INDICADORECONOMICField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://ws.sdde.bccr.fi.cr")]
public partial class stringDatos_de_INGC011_CAT_INDICADORECONOMICINGC011_CAT_INDICADORECONOMIC
{

    private ushort cOD_INDICADORINTERNOField;

    private System.DateTime dES_FECHAField;

    private decimal nUM_VALORField;

    /// <remarks/>
    public ushort COD_INDICADORINTERNO
    {
        get
        {
            return this.cOD_INDICADORINTERNOField;
        }
        set
        {
            this.cOD_INDICADORINTERNOField = value;
        }
    }

    /// <remarks/>
    public System.DateTime DES_FECHA
    {
        get
        {
            return this.dES_FECHAField;
        }
        set
        {
            this.dES_FECHAField = value;
        }
    }

    /// <remarks/>
    public decimal NUM_VALOR
    {
        get
        {
            return this.nUM_VALORField;
        }
        set
        {
            this.nUM_VALORField = value;
        }
    }
}

Once you have that class ready, you should be able to get the deserialize to work.

I used something like this:

class Program
{
    static void Main(string[] args)
    {   
        string myxml = $@"
                <string  xmlns = ""http://ws.sdde.bccr.fi.cr""> 
                  <Datos_de_INGC011_CAT_INDICADORECONOMIC>
                    <INGC011_CAT_INDICADORECONOMIC>
                      <COD_INDICADORINTERNO>317</COD_INDICADORINTERNO>
                      <DES_FECHA>2022-04-20T00:00:00-06:00</DES_FECHA>
                      <NUM_VALOR>650.10000000</NUM_VALOR>
                    </INGC011_CAT_INDICADORECONOMIC>
                  </Datos_de_INGC011_CAT_INDICADORECONOMIC>
                </string>
                
                ";
        var res = XMLToObject(myxml, typeof(@string));
    }
    public static object XMLToObject(string xml, Type type, string xmlnamespace = "http://ws.sdde.bccr.fi.cr")
    {
        object result = null;
        using (var stream = new StringReader(xml))
        {
            using(var reader = new XmlTextReader(stream))
            {
                var xmlRootAttr = new XmlRootAttribute();
                xmlRootAttr.Namespace = xmlnamespace;
                xmlRootAttr.ElementName = type.Name;
                var serializer = new XmlSerializer(type, xmlRootAttr);
                result = serializer.Deserialize(reader);
            }
        }                
        return result;
    }
}

enter image description here

psj01
  • 3,075
  • 6
  • 32
  • 63
  • 1
    Ensure that you've called `Dispose` on any object that implements `IDisposable`. The following may be helpful: [using statement](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement) – Tu deschizi eu inchid Jun 19 '22 at 14:09
1

The problem is that the "outer" XML is a wrapper for the "inner" one, which is encoded as text node for the first. In other words, that's NOT a single document, rather two different documents nested.

My guess is about the outer one, which looks like a descriptor for what itself contains.

That being said, you can extract what you need in a two-step job:

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

using( var client = new HttpClient() )
{
    client.DefaultRequestHeaders
        .Accept
        .Add( new MediaTypeWithQualityHeaderValue( "text/xml" ) );
    var request = new HttpRequestMessage( HttpMethod.Get, " ... " );
    var returnedXml = client.SendAsync( request ).Result.Content.ReadAsStringAsync().Result;

    Console.WriteLine( returnedXml );

    //create a XML DOM from the returned payload
    XDocument xdoc_outer = XDocument.Parse(returnedXml );

    //extract the inner document as text (being a true-text node)
    string inner_text = (string)xdoc_outer.Root;

    //deserialize the text straight to a POCO shaped accordingly
    var serializer = new XmlSerializer(typeof(Datos_de_INGC011_CAT_INDICADORECONOMIC));
    using (var reader = XmlReader.Create(new StringReader(inner_text)))
    {
        //'result' contains the deserialized POCO, unless something went wrong!
        var result = (Datos_de_INGC011_CAT_INDICADORECONOMIC)serializer.Deserialize(reader);
        
    }
}

Probably there are better yet compact ways to achieve the same result, but I think that's pretty clear.

As final note, I reccomend to use async/await than accessing the data through the Result property.

Mario Vernari
  • 6,649
  • 1
  • 32
  • 44