I am getting web service responses in JSON, the strings like this:
{
"adjusted": true,
"queryCount": 2,
"request_id": "6a7e466379af0a71039d60cc78e72282",
"results": [
{
"c": 75.0875,
"h": 75.15,
"l": 73.7975,
"n": 1,
"o": 74.06,
"t": 1577941200000,
"v": 135647456,
"vw": 74.6099
},
{
"c": 74.3575,
"h": 75.145,
"l": 74.125,
"n": 1,
"o": 74.2875,
"t": 1578027600000,
"v": 146535512,
"vw": 74.7026
}
],
"resultsCount": 2,
"status": "OK",
"ticker": "AAPL"
}
When I use my own C# objects (below) they look like this and I reserialize fine:
public class Aggregates
{
[JsonProperty("ticker")]
public string Ticker { get; set; }
[JsonProperty("queryCount")]
public int QueryCount { get; set; }
[JsonProperty("resultsCount")]
public int ResultsCount { get; set; }
[JsonProperty("adjusted")]
public bool Adjusted { get; set; }
[JsonProperty("results")]
public List<Aggregate> Aggregatelist { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("request_id")]
public string RequestId { get; set; }
[JsonProperty("count")]
public int Count { get; set; }
}
public class Aggregate
{
/// <summary>
/// The trading volume of the symbol in the given time period.
/// </summary>
[JsonProperty("v")]
public object Volume { get; set; }
/// <summary>
/// The volume weighted average price.
/// </summary>
[JsonProperty("vw")]
public double VolumeWeightedw { get; set; }
/// <summary>
/// The open price for the symbol in the given time period.
/// </summary>
[JsonProperty("o")]
public double Open { get; set; }
/// <summary>
/// The close price for the symbol in the given time period.
/// </summary>
[JsonProperty("c")]
public double Close { get; set; }
/// <summary>
/// The highest price for the symbol in the given time period.
/// </summary>
[JsonProperty("h")]
public double High { get; set; }
/// <summary>
/// The lowest price for the symbol in the given time period.
/// </summary>
[JsonProperty("l")]
public double Low { get; set; }
/// <summary>
/// The Unix Msec timestamp for the start of the aggregate window.
/// </summary>
[JsonProperty("t")]
public long StartTime { get; set; }
/// <summary>
/// The number of transactions in the aggregate window.
/// </summary>
[JsonProperty("n")]
public int TransactionsNum { get; set; }
}
However, I am forced now to use a library that has its own C# object Quote instead of my Aggregate object. Redundant to say that I cannot change Quote object.
public class Quote : IQuote
{
public Quote();
public DateTime Date { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Volume { get; set; }
}
public interface IQuote
{
DateTime Date { get; }
decimal Open { get; }
decimal High { get; }
decimal Low { get; }
decimal Close { get; }
decimal Volume { get; }
}
I could just copy data from Aggregate to Quote for all objects but that could be millions of objects, and I have a gut feeling that it is possible to deserialize JSON directly to Quote object in a very concise way, I just could not find how...
CodePudding user response:
you can try this
var json=... your json
List<Quote> quotes = JsonConvert
.DeserializeObject<Aggregates>(json)
.Aggregatelist.Select(a => new Quote
{
Date = UnixTimeToDateTime(a.StartTime),
Open = (decimal)a.Open,
High = (decimal)a.High,
Low = (decimal)a.Low,
Close = (decimal)a.Close,
Volume = (decimal)a.VolumeWeightedw
}).ToList();
public static DateTime UnixTimeToDateTime(long unixtime)
{
var dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
return dtDateTime.AddMilliseconds(unixtime).ToLocalTime();
}
output
[
{
"Date": "2020-01-02T01:30:00-03:30",
"Open": 74.06,
"High": 75.15,
"Low": 73.7975,
"Close": 75.0875,
"Volume": 74.6099
},
{
"Date": "2020-01-03T01:30:00-03:30",
"Open": 74.2875,
"High": 75.145,
"Low": 74.125,
"Close": 74.3575,
"Volume": 74.7026
}
]
CodePudding user response:
You should be able to do by extending DefaultContractResolver
class Program
{
static void Main(string[] args)
{
var jsonStr =
"[{'c': 75.0875,'h': 75.15,'l': 73.7975,'n': 1,'o': 74.06,'t': 1577941200000,'v': 135647456,'vw': 74.6099},"
"{'c': 74.3575,'h': 75.145,'l': 74.125,'n': 1,'o': 74.2875,'t': 1578027600000,'v': 146535512,'vw': 74.7026}]";
var jsonResolver = new PropertyRenameAndIgnoreSerializerContractResolver();
jsonResolver.RenameProperty(typeof(Quote), "Close","c");
jsonResolver.RenameProperty(typeof(Quote), "Volume","vw");
jsonResolver.RenameProperty(typeof(Quote), "Open","o");
jsonResolver.RenameProperty(typeof(Quote), "Low","l");
jsonResolver.RenameProperty(typeof(Quote), "High","h");
var serializerSettings = new JsonSerializerSettings();
serializerSettings.ContractResolver = jsonResolver;
var objList = JsonConvert.DeserializeObject<List<Quote>>(jsonStr, serializerSettings);
}
}
This is a sample implementation
public class PropertyRenameAndIgnoreSerializerContractResolver : DefaultContractResolver
{
private readonly Dictionary<Type, HashSet<string>> _ignores;
private readonly Dictionary<Type, Dictionary<string, string>> _renames;
public PropertyRenameAndIgnoreSerializerContractResolver()
{
_ignores = new Dictionary<Type, HashSet<string>>();
_renames = new Dictionary<Type, Dictionary<string, string>>();
}
public void IgnoreProperty(Type type, params string[] jsonPropertyNames)
{
if (!_ignores.ContainsKey(type))
_ignores[type] = new HashSet<string>();
foreach (var prop in jsonPropertyNames)
_ignores[type].Add(prop);
}
public void RenameProperty(Type type, string propertyName, string newJsonPropertyName)
{
if (!_renames.ContainsKey(type))
_renames[type] = new Dictionary<string, string>();
_renames[type][propertyName] = newJsonPropertyName;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName))
{
property.ShouldSerialize = i => false;
property.Ignored = true;
}
if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName))
property.PropertyName = newJsonPropertyName;
return property;
}
private bool IsIgnored(Type type, string jsonPropertyName)
{
if (!_ignores.ContainsKey(type))
return false;
return _ignores[type].Contains(jsonPropertyName);
}
private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName)
{
Dictionary<string, string> renames;
if (!_renames.TryGetValue(type, out renames) || !renames.TryGetValue(jsonPropertyName, out newJsonPropertyName))
{
newJsonPropertyName = null;
return false;
}
return true;
}
}
and simplified Quote class for this example.
public class Quote
{
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Volume { get; set; }
}
CodePudding user response:
You can use a custom data contractor like so:
public class QuoteDataContractResolver : DefaultContractResolver
{
public static readonly QuoteDataContractResolver Instance = new QuoteDataContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType == typeof(Quote))
{
switch (property.PropertyName)
{
case "Date":
property.PropertyName = "d";
break;
case "Open":
property.PropertyName = "o";
break;
//and so on for all the other props
}
}
return property;
}
}
And use it like so:
Quote q = new Quote { };
var settings = new JsonSerializerSettings
{
ContractResolver = QuoteDataContractResolver.Instance
};
//Serialize
var jsonStr = JsonConvert.SerializeObject(q, settings);
//Deserialize
q = JsonConvert.DeserializeObject<Quote>(jsonStr, settings);