using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static partial class JsonExtensions
public static IEnumerable<T> DeserializeArrayItems<T>(this JsonSerializer serializer, JsonReader reader)
if (reader.MoveToContent().TokenType == JsonToken.Null)
if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("Current token {0} is not an array at path {1}", reader.TokenType, reader.Path));
switch (reader.TokenType)
yield return serializer.Deserialize<T>(reader);
throw new JsonReaderException(string.Format("Unclosed array at path {0}", reader.Path));
public static JsonReader MoveToContent(this JsonReader reader)
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment && reader.Read())
public class PurpleAirData
public PurpleAirData(DateTime createdAt, double airQuality)
this.CreatedAt = createdAt;
this.AirQuality = airQuality;
public DateTime CreatedAt { get; set; }
public double AirQuality { get; set; }
public double? Temperature { get; set; }
public double? Humidity { get; set; }
public Channel channel { get; set; }
public List<PurpleAirData> feeds { get; set; }
class PurpleAirListConverter : JsonConverter
[JsonProperty("created_at")]
public DateTime? CreatedAt { get; set; }
public double? AirQuality { get; set; }
public double? Temperature { get; set; }
public double? Humidity { get; set; }
public override bool CanConvert(Type objectType)
return objectType == typeof(List<PurpleAirData>);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
if (reader.MoveToContent().TokenType == JsonToken.Null)
var list = existingValue as List<PurpleAirData> ?? new List<PurpleAirData>();
var query = from dto in serializer.DeserializeArrayItems<PurpleAirDataDTO>(reader)
where dto != null && dto.CreatedAt != null && dto.AirQuality != null
select new PurpleAirData(dto.CreatedAt.Value, dto.AirQuality.Value) { Humidity = dto.Humidity, Temperature = dto.Temperature };
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
throw new NotImplementedException();
public static void Test()
using (var textReader = new StringReader(GetJson()))
root = DeserializePurpleAirDataFile(textReader);
var newJson = JsonConvert.SerializeObject(root, Formatting.Indented);
Console.WriteLine("Deserialised purple air data: ");
Console.WriteLine(newJson);
Assert.IsTrue(root.feeds.Count == JToken.Parse(GetJson()).SelectTokens("feeds[?(@.created_at && @.field8)]").Count());
static RootObject DeserializePurpleAirDataFile(TextReader textReader)
var settings = new JsonSerializerSettings
Converters = { new PurpleAirListConverter() },
NullValueHandling = NullValueHandling.Ignore,
var serializer = JsonSerializer.CreateDefault(settings);
using (var reader = new JsonTextReader(textReader) { CloseInput = false })
return serializer.Deserialize<RootObject>(reader);
""name"": ""AirMonitor_e81a"",
""field1"": ""PM1.0 (ATM)"",
""field2"": ""PM2.5 (ATM)"",
""field3"": ""PM10.0 (ATM)"",
""field6"": ""Temperature"",
""field7"": ""Humidity"",
""field8"": ""PM2.5 (CF=1)"",
""created_at"": ""2018-11-09T00:35:34Z"",
""updated_at"": ""2018-11-09T00:35:35Z"",
""created_at"": ""2019-01-10T23:56:09Z"",
""created_at"": ""2019-01-10T23:57:29Z"",
//""created_at"": ""2019-01-10T23:57:29Z"",
""created_at"": ""2019-01-10T23:57:29Z"",
""created_at"": ""2019-01-26T00:14:04Z"",
public int id { get; set; }
public string name { get; set; }
public string latitude { get; set; }
public string longitude { get; set; }
public string field1 { get; set; }
public string field2 { get; set; }
public string field3 { get; set; }
public string field4 { get; set; }
public string field5 { get; set; }
public string field6 { get; set; }
public string field7 { get; set; }
public string field8 { get; set; }
public DateTime created_at { get; set; }
public DateTime updated_at { get; set; }
public int last_entry_id { get; set; }
public static void Main()
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");