0

I am currently doing a project related to a formula 1 API but sadly I cannot figure out how to serialize my xml content into an object.

The XML I am trying to serialize : https://pastebin.com/nkt9EA1k

My API request and the attempt to serialize it.

        client = new HttpClient();
        HttpResponseMessage response = client.GetAsync("http://ergast.com/api/f1/2022").Result;
        HttpContent responseContent = response.Content;
        XmlSerializer serializer = new XmlSerializer(typeof(Wrapper));
        Wrapper wrapper2;
        string result = File.ReadAllText("temp.txt");//hc.ReadAsStringAsync().Result;
        using (TextReader reader = new StringReader(result))
        {
            wrapper2 = (Wrapper)serializer.Deserialize(reader);
        }
        if(wrapper2 != null)
        {
            SecondWrapper wrapper = wrapper2.MRData;
            string outPut = wrapper.Season;
            foreach (Race r in wrapper.RaceTable)
            {
                outPut += $"\n{r.RaceName}\t{r.Date}\t{r.Circuit.CircuitName}";
            }
            File.WriteAllText("Output.txt", outPut);
        }

My Wrapper class :

public class Wrapper
{
    public SecondWrapper MRData;
}

My SecondWrapper Class :

public class SecondWrapper
{
    public string Season;
    public Race[] RaceTable;
}

My Race class :

public class Race
{
    public string RaceName { set; get; }
    public Circuit Circuit { set; get; }
    public string Date;
    public string Time;
    public StepDateTime FirstPractice { set; get; }
    public StepDateTime SecondPractice { set; get; }
    public StepDateTime ThirdPractice { set; get; }
    public StepDateTime Qualifying { set; get; }
    public StepDateTime Sprint { set; get; }
}

My Circuit class :

public class Circuit
{
    public string CircuitName { set; get; }
    public Location Location { set; get; }
}

My Location class :

public class Location
{
    public double lat { set; get; }
    [XmlAttribute("long")]
    public double Long { set; get; }
    public string Locality { set; get; }
    public string Country { set; get; }
}

My StepDateTime class :

public class StepDateTime
{
    public string Date { set; get; }
    public string Time { set; get; }
}

I tried to make a text file where I deleted the first 2 lines of the XML answer hoping it would fix the issue but it didn't.

PS : I know I'm not using propreties and I am not respecting the c# conventions for the classes but it is temporary just so I can find a way to fix the issue. It is also my first time working with xml answers so I might have done some obvious errors.

Thanks for the help.

DiplomacyNotWar
  • 31,605
  • 6
  • 57
  • 75
  • 1
    .NET has an XSD tool that allows you to generate a schema and then C# classes for the schema instead of having to rely on writing the C# yourself. – Martin Honnen May 30 '22 at 09:00
  • 1
    Your classes don't correspond do your XML. You can auto-generate your classes from the XML as shown in [Generate C# class from XML](https://stackoverflow.com/q/4203540). For instance if I upload your XML to https://xmltocsharp.azurewebsites.net/ I get a plausible set of classes. You will need to deserialize to `[XmlRoot(ElementName="MRData", Namespace="http://ergast.com/mrd/1.5")] public class MRData { /* Properties omitted */ }` – dbc May 30 '22 at 13:23
  • Yes, to **agree** with @dbc, adding an `XmlRoot` attribute as shown in that comment at least gets past the `System.InvalidOperationException`. See: [Deserializing Twitter XML](https://stackoverflow.com/a/1557145/5438626) and [There is an error in XML document](https://stackoverflow.com/a/12672746/5438626) – IVSoftware May 30 '22 at 15:01

1 Answers1

0

You have XML from an API response and your question is How to serialize my xml content into an object. Here's an approach using Newtonsoft.Json NuGet to parse the XML-to-JSON with the JsonConvert.SerializeXNode method then JSON-to-Wrapper.

(Compared to answers using the XmlSerializer this seems a tad more forgiving on the Xml parse.)

string plaintext;
using (StreamReader sr = new StreamReader(
    new WebClient().OpenRead("http://ergast.com/api/f1/2022")))
{
    plaintext = sr.ReadToEnd();
}
        
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Two steps instead of one because IJW.
// XElement parse takes any valid XML (i.e. does not enforce a schema).
XElement xel = XElement.Parse(plaintext);
// Convert the XNode to Json text.
var jsontext =  JsonConvert.SerializeXNode(
        xel, 
        Formatting.Indented); // More readable.
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// JsonConvert is very fault tolerant. It deserializes the
// properies in the model, ignores those that aren't, and
// won't break if queried for properties that it can't provide.
var wrapper = (Wrapper)JsonConvert.DeserializeObject<Wrapper>(jsontext);    

Added/changed these classes and used Race, Circuit Location and StepDateTime as-is:

public class Wrapper
{
    public MRData MRData { get; set; }
}

public class MRData
{
    public RaceTable RaceTable{ get; set; }
}
public class RaceTable
{
    [JsonProperty(propertyName:"@season")]
    public string Season { get; set; }

    [JsonProperty(propertyName: "Race")]
    public Race[] Races { get; set; }
}

Test Code

Console.WriteLine($"Races for the {wrapper.MRData.RaceTable.Season} season.");
Console.WriteLine($"Found {wrapper.MRData.RaceTable.Races.Length} races");
var race0 = wrapper.MRData.RaceTable.Races[0];
Console.WriteLine($"{race0.RaceName} is on {race0.Circuit.CircuitName}"); 

Output

Console Output
Races for the 2022 season.
Found 22 races
Bahrain Grand Prix is on Bahrain International Circuit

Download this code from GitHub

IVSoftware
  • 1,574
  • 11
  • 15