2 Replies - 236 Views - Last Post: 11 May 2021 - 04:32 AM Rate Topic: -----

#1 erburrell   User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 149
  • Joined: 22-December 09

JSON Reading Error

Posted 10 May 2021 - 03:24 PM

All,

I am trying to read a JSON download from a web api call. the program is using WebClient in a console app to run in the background and pull data to write to a OSISoft PI Server. The api call is from VisualSolutions.


The Json string that is received is as follows: (I broke it down for ease of reading)

{
"queryCost":1,
"latitude":39.8967,
"longitude":-79.9754,
"resolvedAddress":"Carmichaels, PA, United States",
"address":"Carmichaels, PA",
"timezone":"America/New_York",
"tzoffset":-4.0,
"days":[
	{"datetime":"2021-05-10",
	"datetimeEpoch":1620619200,
	"tempmax":58.2,
	"tempmin":40.3,
	"temp":47.5,
	"feelslikemax":58.2,
	"feelslikemin":35.5,
	"feelslike":45.9,
	"dew":37.4,
	"humidity":68.8,
	"precip":0.0,
	"precipprob":0.0,
	"preciptype":["rain"],
	"snow":0.0,
	"snowdepth":0.0,
	"windgust":23.0,
	"windspeed":8.9,
	"winddir":286.5,
	"pressure":1017.4,
	"cloudcover":78.3,
	"visibility":11.1,
	"solarradiation":420.9,
	"solarenergy":27.5,
	"uvindex":10.0,
	"severerisk":10.0,
	"sunrise":"06:09:33",
	"sunriseEpoch":1620641373,
	"sunset":"20:23:41",
	"sunsetEpoch":1620692621,
	"moonphase":1.0,
	"conditions":"Partially cloudy",
	"description":"Partly cloudy throughout the day.",
	"icon":"partly-cloudy-day",
	"stations":["KMGW","KAGC","KAFJ"],
	"source":"comb"}
	],
"stations":
	{
	"KMGW":
		{
		"distance":27869.0,
		"latitude":39.65,
		"longitude":-79.92,
		"useCount":0,
		"id":"KMGW",
		"name":"KMGW",
		"quality":100,
		"contribution":0.0
		},
	"KAGC":
		{
		"distance":50609.0,
		"latitude":40.35,
		"longitude":-79.93,
		"useCount":0,
		"id":"KAGC",
		"name":"KAGC",
		"quality":100,
		"contribution":0.0
		},
	"KAFJ":
		{
		"distance":36728.0,
		"latitude":40.13,
		"longitude":-80.28,
		"useCount":0,
		"id":"KAFJ",
		"name":"WASHINGTON, PA",
		"quality":99,
		"contribution":0.0
		},
	"C8839":
		{
		"distance":16966.0,
		"latitude":39.928,
		"longitude":-80.17,
		"useCount":0,
		"id":"C8839",
		"name":"CW8839 Waynesburg PA US",
		"quality":0,
		"contribution":0.0
		}
	},
"currentConditions":	{
	"datetime":"17:04:26",
	"datetimeEpoch":1620680666,
	"temp":57.8,
	"feelslike":57.8,
	"humidity":48.0,
	"dew":38.2,
	"precip":0.0,
	"snowdepth":0.0,
	"windgust":13.0,
	"windspeed":4.0,
	"winddir":311.0,
	"pressure":1019.0,
	"visibility":9.9,
	"cloudcover":88.0,
	"conditions":"Partially cloudy",
	"icon":"partly-cloudy-day",
	"stations":
		[
		"KMGW",
		"C8839"
		],
	"sunrise":"06:09:33",
	"sunriseEpoch":1620641373,
	"sunset":"20:23:41",
	"sunsetEpoch":1620692621,
	"moonphase":1.0
	}
}


The computer code is as follows:


using System;
using System.IO;
using System.Threading.Tasks;
using System.Net;
using System.Data;
using Newtonsoft.Json;
using OSIsoft.AF;
using OSIsoft.AF.PI;
using OSIsoft.AF.Asset;
using OSIsoft.AF.Data;
using OSIsoft.AF.Time;


namespace DataRetriever
{
    class Program
    {
        static void Main(string[] args)
        {
            
  
            
            try // Get data for current conditions.
            {
                //Set url for Visual Crossing site for weather download.
                var url = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Carmichaels%2C%20PA/today?unitGroup=us&key=MyKeyIsActuallyHere&options=nonulls&include=current";
                var currentWeather = _download_serialized_json_data<CurrentConditions>(url);  // Downloads and serializes the information into a class.
                WriteToLog(currentWeather.ToString());

                // Visual crossings provides a value of seconds from epoch (01/01/1970) for calculating the timestamp.  Convert to a standard DateTime Value.
                DateTime dateTime;
                dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddSeconds(currentWeather.currentConditions.datetimeEpoch).AddHours(-4);


                // Create a connection to the PI Server
                string myServer = "PINODE-01";
                PISystem myPiSys = new PISystems().DefaultPISystem;
                PIServer myPiServer = PIServer.FindPIServer(myPiSys, myServer);
                myPiServer.Connect();

                // Set the working database.
                AFDatabase myDatabase = myPiSys.Databases["HTEC-AF1"];

                // Create AFValues for each value to write.
                AFValue tempValue = new AFValue(currentWeather.currentConditions.temp, new AFTime(dateTime));
                AFValue pressValue = new AFValue(GetSitePressure(currentWeather.currentConditions.pressure), new AFTime(dateTime));
                AFValue humValue = new AFValue(currentWeather.currentConditions.humidity, new AFTime(dateTime));

                // Select element and write values to attributes.
                AFElement ambElement = myDatabase.Elements["Ambient Conditions"];
                System.Guid ambAttTempId = ambElement.Attributes["Temperature"].ID;
                System.Guid ambAttHumId = ambElement.Attributes["Humidity"].ID;
                System.Guid ambAttPressId = ambElement.Attributes["Barometric Pressure"].ID;
                ambElement.Attributes[ambAttTempId].SetValue(tempValue);
                ambElement.Attributes[ambAttHumId].SetValue(humValue);
                ambElement.Attributes[ambAttPressId].SetValue(pressValue);

                // Dsiconnectr from PI Server.
                myPiServer.Disconnect();


                WriteToLog("Wrote ambient condition values at "
                        + DateTime.Now
                        + " for timestamp: " + dateTime
                        + ".  Temp = " + currentWeather.currentConditions.temp
                        + ", Humidity=" + currentWeather.currentConditions.humidity
                        + ", Pressure=" + GetSitePressure(currentWeather.currentConditions.pressure) + ".");

            }
            catch (Exception e)
            {
                // Catch exceptions and write to console.
                WriteToLog(e.ToString());
            }


            try     // Get data for daily forecast.
            {
                //Set url for Visual Crossing site for weather download.
                var url = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/Carmichaels%2C%20PA/today?unitGroup=us&key=MyKeyIsActuallyHere&include=fcst";
                var forecastWeather = _download_serialized_json_data<ForecastConditions>(url);  // Downloads and serializes the information into a class.


                // Visual crossings provides a value of seconds from epoch (01/01/1970) for calculating the timestamp.  Convert to a standard DateTime Value.
                DateTime fcDateTime;
                fcDateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddSeconds(forecastWeather.days[0].datetimeEpoch).AddHours(-4);


                // Create a connection to the PI Server
                string myServer = "PINODE-01";
                PISystem myPiSys = new PISystems().DefaultPISystem;
                PIServer myPiServer = PIServer.FindPIServer(myPiSys, myServer);
                myPiServer.Connect();

                // Set the working database.
                AFDatabase myDatabase = myPiSys.Databases["HTEC-AF1"];

                // Create AFValues for each value to write.
                AFValue fcTempMaxValue = new AFValue(forecastWeather.days[0].tempmax, new AFTime(fcDateTime));
                AFValue fcTempMinValue = new AFValue(forecastWeather.days[0].tempmin, new AFTime(fcDateTime));
                AFValue fcPressValue = new AFValue(GetSitePressure(forecastWeather.days[0].pressure), new AFTime(fcDateTime));
                AFValue fcHumValue = new AFValue(forecastWeather.days[0].humidity, new AFTime(fcDateTime));

                // Select element and write values to attributes.
                AFElement ambElement = myDatabase.Elements["Ambient Conditions"];
                ambElement.Attributes["Forecast Max Temp"].SetValue(fcTempMaxValue);
                ambElement.Attributes["Forecast Min Temp"].SetValue(fcTempMinValue);
                ambElement.Attributes["Forecast Humidity"].SetValue(fcHumValue);
                ambElement.Attributes["Forecast Barometric Pressure"].SetValue(fcPressValue);

                // Dsiconnectr from PI Server.
                myPiServer.Disconnect();


                WriteToLog("Wrote forecast condition values at "
                        + DateTime.Now
                        + " for timestamp: " + fcDateTime
                        + ". Max Temp = " + forecastWeather.days[0].tempmax
                        + ", Min Temp = " + forecastWeather.days[0].tempmin
                        + ", Humidity=" + forecastWeather.days[0].humidity
                        + ", Pressure=" + GetSitePressure(forecastWeather.days[0].pressure) + ".");
            }
            catch (Exception e)
            {
                // Catch exceptions and write to console.
                WriteToLog(e.ToString());
            }
        
        
        }

        private static T _download_serialized_json_data<T>(string url) where T : new()
        {
            using (var w = new WebClient())
            {
                var json_data = string.Empty;
                // attempt to download JSON data as a string
                try
                {
                    json_data = w.DownloadString(url);
                }
                catch (Exception e) 
                {
                    Console.WriteLine(e);
                }
                // if string with JSON data is not empty, deserialize it to class and return its instance 
                return !string.IsNullOrEmpty(json_data) ? JsonConvert.DeserializeObject<T>(json_data) : new T();
            }
        }

        private static decimal GetSitePressure(decimal pressure)
        {
            decimal pressureInPsi;

            pressureInPsi = Convert.ToDecimal(Convert.ToSingle(pressure) / 1000.0f * 25.5299f * Math.Exp(-0.0000366f * 1122.0f) * 14.50377f / 25.5299f);

            return pressureInPsi;
        }

        public static int WriteToLog(string TextToWrite)
        {
            try
            {
                string fileName = @"C:\MiscPrograms\DataRetriever\DataRetrieverLog.txt";
                if (File.Exists(fileName))
                {
                    using (StreamWriter fileWriter = File.AppendText(fileName))
                    {
                        fileWriter.WriteLine(TextToWrite);
                    }
                }
                else
                {
                    using (StreamWriter fileWriter = File.CreateText(fileName))
                    {
                        fileWriter.WriteLine(TextToWrite);
                    }
                }
                return 0;
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return 1;
            }

        }
    }
}

// Different file/class...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataRetriever
{
    public class PjmData
    {
        public Dictionary<string, string> links;
        public List<PjmPricing> items;
        public SearchSpecs searchSpecifications;
        public int totalRows;
    }

    public class PjmPricing
    {
        public string datetime_beginning_ept;
        public decimal system_energy_price_da;
        public decimal total_lmp_da;
        public decimal congestion_price_da;
        public decimal marginal_loss_price_da;
    }

    public class SearchSpecs
    {
        public int rowCount;
        public string order;
        public int startRow;
        public bool isActiveMetadata;
        public List<string> fields;
        public Dictionary<string, string> filters;
        public long pnode_id;
    }
}




The error I am getting is:
Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.Dictionary`2[System.String,System.String]]' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'days', line 1, position 188.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureArrayContract(JsonReader reader, Type objectType, JsonContract contract)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at DataRetriever.Program._download_serialized_json_data[T](String url) in C:\Users\rburrell\source\repos\DataRetriever\Program.cs:line 147
at DataRetriever.Program.Main(String[] args) in C:\Users\rburrell\source\repos\DataRetriever\Program.cs:line 27

The odd thing is that the second call works fine. THe JSON string received for the second call is:
{
"queryCost":1,
"latitude":39.8967,
"longitude":-79.9754,
"resolvedAddress":"Carmichaels, PA, United States",
"address":"Carmichaels, PA",
"timezone":"America/New_York",
"tzoffset":-4.0,
"description":"Similar temperatures continuing with no rain expected.",
"days":
	[
	{
	"datetime":"2021-05-10",
	"datetimeEpoch":1620619200,
	"tempmax":56.9,
	"tempmin":43.1,
	"temp":51.6,
	"feelslikemax":56.9,
	"feelslikemin":40.7,
	"feelslike":51.2,
	"dew":37.9,
	"humidity":57.0,
	"precip":0.0,
	"precipprob":0.0,
	"precipcover":null,
	"preciptype":["rain"],
	"snow":0.0,
	"snowdepth":0.0,
	"windgust":15.0,
	"windspeed":8.5,
	"winddir":267.4,
	"pressure":1017.8,
	"cloudcover":61.6,
	"visibility":15.0,
	"solarradiation":440.8,
	"solarenergy":25.4,
	"uvindex":9.0,
	"severerisk":10.0,
	"sunrise":"06:09:33",
	"sunriseEpoch":1620641373,
	"sunset":"20:23:41",
	"sunsetEpoch":1620692621,
	"moonphase":1.0,
	"conditions":"Partially cloudy",
	"description":"Partly cloudy throughout the day.",
	"icon":"partly-cloudy-day",
	"stations":null,
	"source":"fcst"
	}
	]
}


Any help is greatly appreciated!

Is This A Good Question/Topic? 0
  • +

Replies To: JSON Reading Error

#2 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7788
  • View blog
  • Posts: 26,018
  • Joined: 05-May 12

Re: JSON Reading Error

Posted 10 May 2021 - 08:42 PM

In your second JSON chunk "stations" is null, while in your first JSON chunk, it has an array of strings. I'm assuming that in your CurrentCondidions class is expecting stations to be a dictionary, rather than a string array. I'm forced to make that assumption because you didn't show us how your CurrentConditions class is declared.
Was This Post Helpful? 0
  • +
  • -

#3 erburrell   User is offline

  • D.I.C Head
  • member icon

Reputation: 10
  • View blog
  • Posts: 149
  • Joined: 22-December 09

Re: JSON Reading Error

Posted 11 May 2021 - 04:32 AM

Sorry, here are the other classes.

using System;
using System.Collections.Generic;
using System.Text;

/*
 * This class is used strictly as a model for the JSON download from Visual Crossing Weather page.
 * */
namespace DataRetriever
{
    public class CurrentConditions
    {
        public int? queryCost;
        public decimal? latitude;
        public decimal? longitude;
        public string resolvedAddress;
        public string address;
        public string timezone;
        public decimal tzoffset;
        public Dictionary<string, Dictionary<string, string>> days;
        public Dictionary<string, Dictionary<string, string>> alerts;
        public Dictionary<string, Dictionary<string, string>> stations;
        public CurrentConditionsData currentConditions;
    }

    public class CurrentConditionsData
    {
        public string datetime;
        public long datetimeEpoch;
        public decimal temp;
        public decimal? feelslike;
        public decimal humidity;
        public decimal? dew;
        public decimal? precip;
        public string precipprob;
        public string snow;
        public decimal? snowdepth;
        public string preciptype;
        public decimal? windgust;
        public decimal? windspeed;
        public decimal? winddir;
        public decimal pressure;
        public decimal? visibility;
        public decimal? cloudcover;
        public string solarradiation;
        public string solarenergy;
        public string conditions;
        public string icon;
        public string[] stations;
        public string sunrise;
        public long? sunriseEpoch;
        public string sunset;
        public long? sunsetEpoch;
        public decimal? moonphase;
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DataRetriever
{
    public class ForecastConditions
    {
        public int? queryCost;
        public decimal? latitude;
        public decimal? longitude;
        public string resolvedAddress;
        public string address;
        public string timezone;
        public decimal? tzoffset;
        public List<ForecastConditionsData> days;
    }

    public class ForecastConditionsData
    {
        public string datetime;
        public long datetimeEpoch;
        public decimal tempmax;
        public decimal tempmin;
        public decimal temp;
        public decimal? feelslikemax;
        public decimal? feelslikemin;
        public decimal? feelslike;
        public decimal? dew;
        public decimal humidity;
        public decimal? precip;
        public string precipprob;
        public string precipcover;
        public string[] preciptype;
        public string snow;
        public decimal? snowdepth;
        public decimal? windgust;
        public decimal? windspeed;
        public decimal? winddir;
        public decimal pressure;
        public decimal? cloudcover;
        public decimal? visibility;
        public string solarradiation;
        public string solarenergy;
        public string sunrise;
        public long sunriseEpoch;
        public string sunset;
        public long? sunsetEpoch;
        public decimal? moonphase;
        public string conditions;
        public string icon;
        public string stations;
        public string source;
        public decimal? hours;
        
    }
}



Was This Post Helpful? 0
  • +
  • -

Page 1 of 1