private List <ObservationDto> ConvertToDTO(response xmlObjs,
                                                   IList <string> icaos)
        {
            var            dtos = new List <ObservationDto>();
            ObservationDto obs  = null;

            if (xmlObjs.data?.METAR != null)
            {
                foreach (var metarXML in xmlObjs.data.METAR)
                {
                    obs = dtos.Where(o => o.ICAO == metarXML.station_id).FirstOrDefault();
                    if (obs == null)
                    {
                        obs = new ObservationDto();
                        ParseIdentifier(obs, metarXML);
                        ParseGeographicData(obs, metarXML);
                        dtos.Add(obs);
                    }
                    var metarDTO = new METARDto();
                    ParseCurrentWeatherData(metarDTO, metarXML);
                    Parse3HourWeatherData(metarDTO, metarXML);
                    Parse6HourWeatherData(metarDTO, metarXML);
                    Parse24HourWeatherData(metarDTO, metarXML);
                    ParseQualityControlFlags(metarDTO, metarXML);
                    obs.METAR.Add(metarDTO);
                }
            }

            dtos.AddRange(ParserHelpers.GetMissingStations(dtos, icaos));
            return(dtos);
        }
        public List <ObservationDto> Parse(string data, IList <string> icaos)
        {
            if (String.IsNullOrWhiteSpace(data))
            {
                throw new ArgumentException($"'{nameof(data)}' cannot be null or empty.");
            }

            if (icaos == null ||
                icaos.Count == 0)
            {
                throw new ArgumentException($"'{nameof(icaos)}' cannot be null or empty.");
            }

            var obsDto      = new List <ObservationDto>();
            var foundHeader = false;
            var fieldOrder  = new List <METARCSVField>();

            var splitData = data.Split(new char[] { '\n' });

            // Expect the format of the file to be the following Top few lines are status/error
            // details on the request Header line with all of the columns defined Each line after is
            // an individual observation for any of the ICAOs in the list in chronological order
            // with the most recent observation first
            foreach (var line in splitData)
            {
                if (String.IsNullOrWhiteSpace(line))
                {
                    continue;
                }
                if (!foundHeader &&
                    line.Contains(METARCSVField.raw_text.Name))
                {
                    fieldOrder = GetFieldOrder(line);
                    if (fieldOrder.Count > 0)
                    {
                        foundHeader = true;
                    }
                }
                else if (foundHeader)
                {
                    var stationObs      = GetObservation(line, fieldOrder);
                    var existingStation = obsDto.Where(o => String.Equals(o.ICAO, stationObs.ICAO,
                                                                          StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                    if (existingStation == null)
                    {
                        obsDto.Add(stationObs);
                    }
                    else
                    {
                        existingStation.METAR.Add(stationObs.METAR.FirstOrDefault());
                    }
                }
            }

            obsDto.AddRange(ParserHelpers.GetMissingStations(obsDto, icaos));

            return(obsDto);
        }
        public List <ForecastDto> Parse(string data, IList <string> icaos)
        {
            var      serializer  = new XmlSerializer(typeof(response));
            response responseObj = null;

            using (var streamReader = new StringReader(data))
            {
                responseObj = serializer.Deserialize(streamReader) as response;
            }

            var dtos        = ConvertToDTO(responseObj, icaos);
            var missingDTOs = ParserHelpers.GetMissingStations(dtos, icaos);

            dtos.AddRange(missingDTOs);
            return(dtos);
        }