Firstly, both your input and output JSON are syntactically invalid: they are missing outer braces {
and }
. For the remainder of this answer, I’m going to assume this is a typo in the question.
Assuming you have not done so already, you could install json.net as shown here and then use LINQ to JSON to load and modify your JSON. Using this approach avoids the need to define c# types that perfectly match your JSON.
Your input JSON has two problems:
-
The tokens
"channels.heart-rate.events"
and"channels.location.events"
are arrays of objects for which Json.NET type information has been included. (It is clear from the presence of the"$type"
property that the JSON was originally generated using Json.NET)What you want instead is for these arrays to be reformatted into a single object that contains the item type, an array of item property names, and an array of array of item property values.
-
The type information has been formatted using
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
. You want to add assembly information, converting toFormatterAssemblyStyle.Full
format.
Both of these can be corrected using LINQ-to-JSON. Say you have two streams, Stream inputStream
and Stream outputStream
corresponding to a stream with the JSON to be fixed and a stream in which to store the fixed JSON. Then, introduce the following utility methods:
public static class JsonExtensions
{
const string JsonTypeName = @"$type";
const string JsonValuesName = @"$values";
public static void ReformatCollections(Stream inputStream, Stream outputStream, IEnumerable<string> paths, Func<string, string> typeNameMapper, Formatting formatting)
{
var root = JToken.Load(new JsonTextReader(new StreamReader(inputStream)) { DateParseHandling = DateParseHandling.None });
root = ReformatCollections(root, paths, typeNameMapper);
var writer = new StreamWriter(outputStream);
var jsonWriter = new JsonTextWriter(writer) { Formatting = formatting };
root.WriteTo(jsonWriter);
jsonWriter.Flush();
writer.Flush();
}
public static JToken ReformatCollections(JToken root, IEnumerable<string> paths, Func<string, string> typeNameMapper)
{
foreach (var path in paths)
{
var token = root.SelectToken(path);
var newToken = token.ReformatCollection(typeNameMapper);
if (root == token)
root = newToken;
}
return root;
}
public static JToken ReformatCollection(this JToken value, Func<string, string> typeNameMapper)
{
if (value == null || value.Type == JTokenType.Null)
return value;
var array = value as JArray;
if (array == null)
array = value[JsonValuesName] as JArray;
if (array == null)
return value;
// Extract the item $type and ordered set of properties.
string type = null;
var properties = new Dictionary<string, int>();
foreach (var item in array)
{
if (item.Type == JTokenType.Null)
continue;
var obj = item as JObject;
if (obj == null)
throw new JsonSerializationException(string.Format("Item \"{0}\" was not a JObject", obj.ToString(Formatting.None)));
var objType = (string)obj[JsonTypeName];
if (objType != null && type == null)
type = objType;
else if (objType != null && type != null)
{
if (type != objType)
throw new JsonSerializationException("Too many item types.");
}
foreach (var property in obj.Properties().Where(p => p.Name != JsonTypeName))
{
if (!properties.ContainsKey(property.Name))
properties.Add(property.Name, properties.Count);
}
}
var propertyList = properties.OrderBy(p => p.Value).Select(p => p.Key).ToArray();
var newValue = new JObject();
if (type != null)
newValue["type"] = JToken.FromObject(typeNameMapper(type));
newValue["structure"] = JToken.FromObject(propertyList);
newValue["list"] = JToken.FromObject(array
.Select(o => (o.Type == JTokenType.Null ? o : propertyList.Where(p => o[p] != null).Select(p => o[p]))));
if (value.Parent != null)
value.Replace(newValue);
return newValue;
}
}
Then, at the top level of your console method, you can fix your JSON as follows:
Func<string, string> typeNameMapper = (t) =>
{
if (!t.EndsWith(", Version=1.2.7.0, Culture=neutral, PublicKeyToken=null"))
t = t + ", Version=1.2.7.0, Culture=neutral, PublicKeyToken=null";
return t;
};
var paths = new[]
{
"channels.heart-rate.events",
"channels.location.events"
};
JsonExtensions.ReformatCollections(inputStream, outputStream, paths, typeNameMapper, Formatting.Indented);
Sample fiddle.
5
solved Json data not converting to List