Deserialize iCal using DDay.iCal, can't find the properties - c#

I'm trying to deserialize an iCal file and then map the collection to a custom POCO. The problem is that I'm not sure where the properties are stored.
Here's the Deserialization method
public static IICalendarCollection Deserialize(String iCalUri)
{
var wc = new WebClient();
var result = wc.DownloadString(iCalUri);
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(result)))
{
var serializer = new iCalendarSerializer();
var collection = (iCalendarCollection)serializer.Deserialize(memoryStream, Encoding.UTF8);
return collection;
}
}
Then in my service, I'm trying to map the properties to my POCO, but I can't find the properties.
var url = string.Format(GoogleICalUrl, calendarID);
var feed = iCalUtility.Deserialize(url);
foreach(var e in feed)
{
calendarModel.title = e.WhereTheHellAreThePropertiesKept;
}
Does anyone know the right way to loop through the iCalendarCollection and get all the properties?
I checked the DDay website but it's been down all day.

After some digging, it turns out that IICalendarCollection has a nested Event collection that needs to be parsed as follows.
var url = string.Format(GoogleICalUrl, calendarID);
var feed = iCalUtility.Deserialize(url);
foreach (var ev in feed.SelectMany(e => e.Events))
{
calendarModel.Add(new CalendarModel {description = ev.Description,
title = ev.Summary});
}

Related

How to add property in existing json using System.Text.Json library?

{
"TestData":{
"Year__of__Account":"2019",
"Tax___x0025_":"0.06",
"Buildings__1":"1000",
"Contents__1":"400",
"Total_Insurable_Value":"100",
"Buildings__Prem":"2560.8",
"Contents__Prem":"1707.2",
"YB__1":"1950",
"No__Buildings":"55",
"Location_Sprinklers_YN":"No",
"test":"test"
}
}
In the above sample JSON I want to add a property called "Name" with Value "John" inside property "TestData". How can I achieve this using .net Core 3.0 System.Text.Json library.
I have tried using methods of Utf8JsonWriter but it is creating a new JSON object instead of appending it to the above existing JSON.
using (MemoryStream memoryStream1 = new MemoryStream())
{
using (Utf8JsonWriter utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
{
using (JsonDocument jsonDocument = JsonDocument.Parse(json))
{
utf8JsonWriter1.WriteStartObject();
utf8JsonWriter1.WritePropertyName("Name");
utf8JsonWriter1.WriteStringValue("John");
utf8JsonWriter1.WriteEndObject();
// how can I add above properties to JsonDocument object??
}
}
}
Starting from .NET 6 you can use JsonNode. This is a modifiable, dictionary-backed API to complement the readonly JsonDocument.
For your example, the solution would be as follows:
var jsonNode = JsonNode.Parse(json);
jsonNode["TestData"]["Name"] = "John";
Assuming there may be several properties and you want to add a name only to "TestData" property:
using (MemoryStream memoryStream1 = new MemoryStream())
{
using (Utf8JsonWriter utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
{
using (JsonDocument jsonDocument = JsonDocument.Parse(json))
{
utf8JsonWriter1.WriteStartObject();
foreach (var element in jsonDocument.RootElement.EnumerateObject())
{
if (element.Name == "TestData")
{
utf8JsonWriter1.WritePropertyName(element.Name);
// Staring new object
utf8JsonWriter1.WriteStartObject();
// Adding "Name" property
utf8JsonWriter1.WritePropertyName("Name");
utf8JsonWriter1.WriteStringValue("John");
// Copying existing values from "TestData" object
foreach (var testDataElement in element.Value.EnumerateObject())
{
testDataElement.WriteTo(utf8JsonWriter1);
}
utf8JsonWriter1.WriteEndObject();
}
else
{
element.WriteTo(utf8JsonWriter1);
}
}
utf8JsonWriter1.WriteEndObject();
}
}
var resultJson = Encoding.UTF8.GetString(memoryStream1.ToArray());
}
Here for each property (except for "TestData" property) I write the whole value as is (by calling element.WriteTo(utf8JsonWriter1)), and for "TestData" property I start a new object, add "Name" property and then copy each of the "TestData" object's properties.
P.S. This works, but I'm pretty sure a much better solution should exist.
Here is a possible answer
static void Main(string[] args)
{
var jsonString = #"
{
""TestData"":{
""Year__of__Account"":""2019"",
""Tax___x0025_"":""0.06"",
""Buildings__1"":""1000"",
""Contents__1"":""400"",
""Total_Insurable_Value"":""100"",
""Buildings__Prem"":""2560.8"",
""Contents__Prem"":""1707.2"",
""YB__1"":""1950"",
""No__Buildings"":""55"",
""Location_Sprinklers_YN"":""No"",
""test"":""test""
}
}
";
var jsonDoc = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);
var testDataDict = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonDoc["TestData"].ToString());
testDataDict.Add("Name", "John");
//replace the test data with the modified test data
jsonDoc["TestData"] = testDataDict;
Console.WriteLine(JsonSerializer.Serialize(jsonDoc));
}
I've just created a NuGet package with some hopefully useful extension methods for JsonElement, which allow properties to be added and/or removed. It's based on using the Utf8JsonWriter to create a new mutated JsonElement based on the original, like the answer above.
GitHub repo | NuGet package
var jsonString = "{ \"Name\": \"Andrew\", \"EmailAddress\": \"a#b.com\" }";
var jElement = JsonDocument.Parse(jsonString).RootElement;
jElement = jElementAddProperty("Age", 38)
.AddProperty("Male", true)
.AddProperty("Female", false)
.AddNullProperty("Alien")
.AddProperty("Roles", new string[] { "admin", "user" })
.AddProperty("LastUpdated", DateTime.UtcNow)
.AddProperty("crazyNewObject", new
{
Name = "Hobbies",
Value = "bass guitar and writing c# code"
});
Hopefully someone will find them useful, but if they don't do quite what you need, please enhance and submit a pull request.
Using JsonSerializer to deserialize into a nested dictionary is also possible:
static void Main(string[] args)
{
string testJson = #"
{
""TestData"":{
""Year__of__Account"":""2019"",
""Tax___x0025_"":""0.06"",
""Buildings__1"":""1000"",
""Contents__1"":""400"",
""Total_Insurable_Value"":""100"",
""Buildings__Prem"":""2560.8"",
""Contents__Prem"":""1707.2"",
""YB__1"":""1950"",
""No__Buildings"":""55"",
""Location_Sprinklers_YN"":""No"",
""test"":""test""
}
}";
using (var memoryStream1 = new MemoryStream())
{
using (var utf8JsonWriter1 = new Utf8JsonWriter(memoryStream1))
{
//For each level in json tree an additional dictionary must be added
var jsonDict = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, object>>>(testJson);
jsonDict["TestData"].Add("Name", "John");
JsonSerializer.Serialize<object>(utf8JsonWriter1, jsonDict);
}
string testString = Encoding.UTF8.GetString(memoryStream1.ToArray());
}
}
But note that the new property is always added at the end of the TestData block.

How to parse back data from EventStore stream to correct type?

I stored the event data in EventStore:
var data = new EventData(Guid.NewGuid(),
#event.GetType().ToString(),
true,
#event.ToJsonBytes(),
#event.GetType().ToJsonBytes());
this.connection.AppendToStreamAsync(this.stream + "/" + aggregateId,
ExpectedVersion.Any, data);
Seems to work. But how to parse back the data from EventStore without giving the concrete type?
I tried this way, but this only parses the data for the base class:
foreach (var data in result.Events)
{
var #event = data.Event.Data.ParseJson<Event>();
if (#event != null) // event contains only the base type data
{
events.Add(#event);
}
}
How to get back the data for SomeSpecialEvent that is derived from Event?
There are several Event types and I can't put all in here (storage mechanism should be unaware of the concrete type).
Any ideas how to put the T into the .ParseJson without using generics?
Found a solution by myself:
var result = this.connection.ReadStreamEventsForwardAsync(this.stream + "/" + aggregateId, 0, 4095, false).Result;
foreach (var data in result.Events)
{
var assemblyQualifiedName = data.Event.Metadata.ParseJson<string>();
var type = Type.GetType(assemblyQualifiedName);
var json = Helper.UTF8NoBom.GetString(data.Event.Data);
var #event = JsonConvert.DeserializeObject(json, type) as Event;
if (#event != null)
{
events.Add(#event);
}
}
You need to add the type into metadata when saving:
var type = #event.GetType().AssemblyQualifiedName;
var data = new EventData(
Guid.NewGuid(),
#event.GetType().Name,
true,
#event.ToJsonBytes(),
type.ToJsonBytes());

Reading CollectionJson content in Web Api Project

I have a project similar(Almost identical) to Conference API project which is taking similar approach to the noted project for returning CollectionJson content. I am having difficulty Setting the Collection property of the ReadDocument (Line 30) as it does not have any setter. I could bypass this problem by doing the following change
public CollectionJsonContent(Collection collection)
{
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
collection.Version = "1.0";
Headers.ContentType = new MediaTypeHeaderValue("application/vnd.collection+json");
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
//var readDocument = new ReadDocument(); {IReadDocument.Collection = collection};
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer,collection);
writer.Flush();
}
_memoryStream.Position = 0;
}
Although above code compiles and to some extent sorts out the problem but then again I will have another problem of not being able to consume the JsonCollection content in my controller unit tests. Consider the following unit test code snippet:
using (var request = CreateRequest())
{
var controller = new TestController(DataService) {Request = request};
var temp = await controller.ListAsync(gridSearchData, sampleSearchData);
if ((temp is NotFoundResult) && (sampleCollection.Any()))
{
Assert.Fail("Controller did not return any result but query did");
}
var json = await temp.ExecuteAsync(cancellationTokenSource);
var readDocument = json.Content.ReadAsAsync<ReadDocument>(new[] {new CollectionJsonFormatter()}, cancellationTokenSource).Result;
}
Since I did not set the collection property of ReadDocument readDocument is always empty and I cant read its content.
How do you asynchronously read the contents of JsonCollection on the client side in WEB API projects?
To get a Clear picture of the approach look at the Conference Web Api
and the authors blog
OK all, this has been fixed. The Collection property is now settable again.
I have just pushed release 0.7.0 with this fix, a major naming refactoring as well as a nice improvement to serialization to not write out empty collections.
Please see the release notes for the changes (especially the naming as the package names and namespaces have changed)
As far as I see from your code, you do not serialize a ReadDocument object, but only a property of it (Collection), and then you try to deserialize that value into a new ReadDocument object.
A sample ReadDocument should serialize like this
"{"Collection": [1,2,3,4,5] }"
But you serialize collection, so you get
"[1,2,3,4,5]"
I recommend a surrogate class for serialization like this
class SerializableReadDocument
{
public Collection Collection { get; set; }
}
and update your serialization code like this
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
var readDocument = new SerializableReadDocument() { Collection = collection };
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, readDocument);
writer.Flush();
}
But, this will not resolve your problem when you try to deserialize your output since ReadDocument does not have a settable Collection property, deserialization will either fail, or return a ReadDocument object with an empty Collection.
You can use SerializableReadDocument if you like in your unit tests.
I am looking into this and will come up with a solution hopeful this weekend, which will either be to make it a public setter, or make the setter internal and have public ctor that accepts a collection.
Sorry for the difficulty.

Strange JSON error when trying to fetch album's photos from Facebook

I am using Facebook SDK to load the photos from a specific album like this:
private void LoadAlbum(String albumId)
{
var fb = new FacebookWebClient("token_id");
dynamic albumsPhotos = fb.Get(albumId + "/photos");
List<FacebookImageVo> listOfImages = new List<FacebookImageVo>();
foreach (dynamic imageInfo in albumsPhotos)
{
FacebookImageVo facebookImage = new FacebookImageVo();
if (imageInfo.name != null)
{
facebookImage.Name = imageInfo.name;
}
facebookImage.Id = imageInfo.id;
facebookImage.Source = imageInfo.source;
facebookImage.Picture = imageInfo.picture;
}
rptImages.DataSource = listOfImages;
rptImages.DataBind();
}
I receive a strange error when I try to access name property of the photo (which I am sure it exists):
'System.Collections.Generic.KeyValuePair<string,object>' does not contain a definition for 'name'
Do you know why?
Solution found:
Instead of: foreach (dynamic imageInfo in albumsPhotos) I had to use foreach (dynamic imageInfo in albumsPhotos.data) to be able to access the properties of the Object.

Reach functionality from other class c#

update
I'm writing a silverlight application and I have the following Class "Home", in this class a read a .xml file a write these to a ListBox. In a other class Overview I will show the same .xml file. I know it is stupid to write the same code as in the class "Home".
The problem is, how to reach these data.
My question is how can I reuse the method LoadXMLFile() from another class?
The code.
// Read the .xml file in the class "Home"
public void LoadXMLFile()
{
WebClient xmlClient = new WebClient();
xmlClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(XMLFileLoaded);
xmlClient.DownloadStringAsync(new Uri("codeFragments.xml", UriKind.RelativeOrAbsolute));
}
private void XMLFileLoaded(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
string xmlData = e.Result;
XDocument xDoc = XDocument.Parse(xmlData);
var tagsXml = from c in xDoc.Descendants("Tag") select c.Attribute("name");
List<Tag> lsTags = new List<Tag>();
foreach (string tagName in tagsXml)
{
Tag oTag = new Tag();
oTag.name = tagName;
var tags = from d in xDoc.Descendants("Tag")
where d.Attribute("name").Value == tagName
select d.Elements("oFragments");
var tagXml = tags.ToArray()[0];
foreach (var tag in tagXml)
{
CodeFragments oFragments = new CodeFragments();
oFragments.tagURL = tag.Attribute("tagURL").Value;
//Tags.tags.Add(oFragments);
oTag.lsTags.Add(oFragments);
}
lsTags.Add(oTag);
}
//List<string> test = new List<string> { "a","b","c" };
lsBox.ItemsSource = lsTags;
}
}
Create a class to read the XML file, make references to this from your other classes in order to use it. Say you call it XmlFileLoader, you would use it like this in the other classes:
var xfl = new XmlFileLoader();
var data = xfl.LoadXMLFile();
If I were you, I would make the LoadXMLFile function take a Uri parameter to make it more reusable:
var data = xfl.LoadXMLFile(uriToDownload);
You could create a class whose single responsibility is loading XML and returning it, leaving the class that calls your LoadXmlFile method to determine how to handle the resulting XML.

Categories

Resources