SOAP Response to Tree etc - c#

I have a Question About Soap Responses and how to handle them.
I get a xmlstring from my Response, but have to convert it into this Class to Output into
a specific Window with a view of a tree.
public ExampleTree() { }
public ExampleTree(string nodeContent, List<ExampleTree> subColl)
{
DisplayContent = displayContent;
SubCollection = subCollection;
}
public string DisplayContent { get; set; }
public List<ExampleTree> SubCollection { get; set; }
Does anyone knows which way is the smartest? The hard Thing is that it has to be
generic so I can not use Service References. I just found hardcoded usage, but what
about this case(with subcollections etc.)?

Related

How can I deserialize JSON to a custom type which has a property of type IEnumerable<dynamic>?

I'm writing a console app to retrieve JSON data from a 3rd party API. I have no control over the API data structures or functionality.
Several of the calls I make will return multiple 'pages' of data. The data is a collection of objects of a certain type e.g. User.
I have created classes in my app to match the various data types from the API.
public class User
{
[JsonProperty("id")]
public int ID { get; set; }
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
}
public class FooBar
{
[JsonProperty("foo")]
public string Foo { get; set; }
[JsonProperty("bar")]
public string Bar { get; set; }
}
The API response is always in the same format for these calls. While the actual object types in the "data" array will differ depending on what call has been made.
{
"paging":{"page":1},
"data":[{<object>}, {<object>}, {<object>},...]
}
I have created a class to try to deserialize these. The dynamic[] type for the Data property is for illustrative purposes and I am happy to change it if there is a better approach.
public class ApiResponseObject
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public dynamic[] Data { get; set; }
}
And I would like to have the Data collection resolve to the appropriate type for the objects it contains. e.g.
string userJson = "{\"paging\":{\"page\":1},\"data\":[{\"id\":1,\"first_name\":\"Joe\",\"last_name\":\"Bloggs\"},{\"id\":2,\"first_name\":\"Jane\",\"last_name\":\"Doe\"}]}"; // json string would come from API
string foobarJson = "{\"paging\":{\"page\":1},\"data\":[{\"foo\":\"Lorem\",\"bar\":\"Ipsum\"},{\"foo\":\"Dolor\",\"bar\":\"Amet\"}]}";
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject>(userJson);
var foobarResponse = JsonConvert.DeserializeObject<ApiResponseObject>(foobarJson);
The deserialization succeeds but the Data collection is of type JObject and cannot be cast into the correct type (User, FooBar).
I am trying to avoid having to write specific response object classes for each request if possible.
I will know what type of object I am expecting in the collection when I am requesting it so I could pass that type to the deserializer but I'm not clear on how to achieve that in this particular scenario.
Something like the below psuedo code would be ideal.
var userResponse = JsonConvert.DeserializeObject<ApiResponseObject<User>>(userJson);
Thanks for your help!
You can use the generic type T, like this :
public class ApiResponseObject<T>
{
[JsonProperty("paging")]
public Paging PagingInfo { get; set; }
[JsonProperty("data")]
public T[] Data { get; set; }
}

Deserializing Generic that implements Interface with a List<T> of Interfaces .NET C#

Scenario
I have some XML come down from a service that I want to deserialize.
Depending on what is returned from the service, the XML can vary slightly (with the element names); but the XML always follows a common structure.
Here is a sample of what the XML might look like:
<ATemplate>
<Name>SomeTemplate</Name>
<TemplateItems>
<ATemplateItem>
<Name>SomeTemplateItem</Name>
<TemplateFields>
<ATemplateField>
<Name>SomeTemplateField</Name>
<Colour>Blue</Colour>
</ATemplateField>
... more template fields
</TemplateFields>
</ATemplateItem>
... more template items
</TemplateItems>
</ATemplate>
Using the above XML as an example, I have created a ATemplate class that will deserialize nicely from the XML, using the ATemplateItem and ATemplateField classes accordingly:
public class ATemplate
{
public string Name { get; set; }
public List<ATemplateItem> TemplateItems { get; set; }
}
public class ATemplateItem
{
public string Name { get; set; }
public List<ATemplateField> TemplateFields { get; set; }
}
public class ATemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
I use this code to deserialize:
ATemplate template;
using (TextReader reader = new StringReader(xmlString))
{
template = (ATemplate)new XmlSerializer(typeof(ATemplate)).Deserialize(reader);
}
All good, so far.
Curveball
The same scenario might occur where the XML contains BTemplate, BTemplateItems and BTemplateFields; still following the structure as above.
So I created other classes for this situation:
public class BTemplate { ... }
public class BTemplateItem { ... }
public class BTemplateField { ... }
And made the relevant classes inherit respectively from ITemplate, ITemplateItem and ITemplateField I created, also:
Interfaces
public class ITemplate
{
public string Name { get; set; }
public List<ITemplateItem> TemplateItems { get; set; }
}
public class ITemplateItem
{
public string Name { get; set; }
public List<ITemplateField> TemplateFields { get; set; }
}
public class ITemplateField
{
public string Name { get; set; }
public string Colour { get; set; }
}
This is so I can then create one function, which is able to loop through the ITemplateItems and their ITemplateFields and perform some cool stuff:
public void Foo(ITemplate template)
{
foreach (var item in template.TemplateItems)
{
// do cool stuff
foreach (var field in item.TemplateFields)
{
// do more cool stuff
}
}
}
Some things to note:
In the object that contains the XML, I know what "type" the XML contains - given an Enum I use to identify
I then use a switch statement to run different methods, depending on the said "type"
Generic Method?
Now, rather than deserializing the XML differently in each of those method cases, I would like to use a Generic method to deserialize.
So I created one, like this:
public ITemplate DeserializeTemplate<T>(string xmlString) where T : ITemplate
{
using (TextReader reader = new StringReader(xmlString))
{
return (T)new XmlSerializer(typeof(T)).Deserialize(reader);
}
}
And call it from within the specific methods like so:
var template = DeserializeTemplate<ATemplate>(xmlString);
Then, I can use the ITemplate that it returns, and pass it to Foo(ITemplate template) to go and perform some magic and wizardry.
But...
No compilation errors, as yet - however I get a RunTime error, because it cannot deserialize an Interface.
I gather this is because it's trying to then deserialize the ITemplate's TemplateItems as ITemplateItems.
Can I do the above?
My question is:
How can I get around this issue?
Can I use this Generic deserialize method?
Will I need to treat each one differently in the separate methods?
Will I need to make the Interface generic also, with the types to expect?
I'm banging my head against the desk, so I really hope you lovely SO people can help.
As always, your comments, answers and suggestions are much appreciated :)

Deserializing generically typed classes with Newtonsoft

I have classes that look like this, based on the json being returned by Slack's api:
public class Response<T>
{
public bool ok { get; set; }
public string error { get; set; }
}
public class PostMessage : Response<PostMessage>
{
public string ts { get; set; }
public string channel { get; set; }
public Message message { get; set; }
}
public class ChannelsHistory : Response<ChannelsHistory>
{
public string latest { get; set; }
public List<Message> messages { get; set; }
public bool has_more { get; set; }
}
And I want to write a single method that can bottleneck the call to JsonConvert.DeserializeObject<T>. I don't know too much about the details of the implementation behind that method, but I thought that this would work:
internal static Response<T> GetSlackResponse<T>(List<KeyValuePair<string, string>> parameters = null)
{
Uri slackUri = BuidSlackUri(typeof(T), parameters);
String jsonResponse = GetJson(slackUri);
Response<T> response = JsonConvert.DeserializeObject<Response<T>>(jsonResponse);
if (!response.ok)
{
Aesthetic.Catch("The Slack API failed to respond successfully. " + response.error);
}
return response;
}
Nothing is failing, but not all of the properties I need are being deserialized. For example, a call to GetSlackResponse() will return a Response that has ok set to true, but I won't have access to the Message property of the PostMessage class. I've tried casting (both explicitly and with as), to no avail.
I'm sure I'm missing something simple here, can someone point it out?
Having my various Response class extend a generically typed class was a red herring; it introduced nothing but making the problem more confusing. I only needed the method to be generic, not the type itself.
Changing the relevant line in GetSlackResponse<T>() method from
Response<T> response = JsonConvert.DeserializeObject<Response<T>>(jsonResponse);
to
T response = JsonConvert.DeserializeObject<T>(jsonResponse);
fixed everything. Now my various response types only need to extend my base Response class, and will be properly deserialized by JSON.NET. There is no need for the response to be a generically typed object.

Deserialize object using JSON.NET, but put some properties into a member of the class

I am writing a set of data structures to ingest third-party JSON into (no writing out) using JSON.NET.
I have a case for reading some of the top-level JSON elements into a member object of the object being deserialized into.
My JSON:
{
"Id":1
"Checksum":42
"Name":"adam",
"Hair":true
}
My ideal object structure:
public class EntityHeader
{
int Id { get; set; }
int Checksum { get; set; }
}
public class Entity
{
[HeroicJsonAttribute( "Id", "Checksum" )]
public EntityHeader Header { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
Is there a simple way to achieve this? I will have a number of types which will need this, and I'd hate to have to write a JsonConverter for each.
This question has been asked before, here, but the accepted answer doesn't address the question.
Thanks!
An alternative approach would be to use an EntityHeader field in the Entity class as a backing store for private properties which can be deserialized into:
public class EntityHeader
{
int Id { get; set; }
int Checksum { get; set; }
}
public class Entity
{
private EntityHeader m_Header = new EntityHeader();
public EntityHeader Header { get { return m_Header; } }
[JsonProperty]
private int Id { set { m_Header.Id = value; } }
[JsonProperty]
private int Checksum { set { m_Header.Checksum = value; } }
public string Name { get; set; }
public bool Hair { get; set; }
}
Thus, all the properties in the JSON can be read straight into the Entity object, but consumers of Entity objects have access to a "nicely encapsulated" EntityHeader property.
I haven't tested this, and it may even be kludgey, but it would technically work for me (OP). I am still interested in other answers!
Base on your example you could either; use the adapter pattern:
public class EntityJson
{
int Id { get; set; }
int Checksum { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
// quick/poor example
public class EntityAdapter : IEntity
{
public EntityAdapter(EntityJson model)
{
Header = new Header(); // and populate this objects fields
Name = model.Name; // populate other properties
}
public EntityHeader Header { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
Or abuse the fact that json.net ignores properties not available:
var entity = JsonConvert.Deserialze<Entity>();
var header = JsonConvert.Deserialize<EntityHeader>();
entity.Header = header;
I'm going to go ahead and post this answer which is a little bit too long for a comment, so please take this more as an extended comment than an actual attempt to answer your specific question. And of course, you know your requirements best so this is just my considered opinion :)
With that in mind, my advice is:
Don't do this.
I would instead create a simple DTO class that has a 1-1 relationship to the JSON being received; and I'd put all my validation attributes on the properties of that class.
Once I had deserialised the JSON into this simple DTO, I would then use a mapping layer of some kind (roll your own or use Automapper, etc) to map this DTO into a more meaningful structure such as your Entity class.
My reasoning behind this is because unless your Entity class is itself only a simple DTO (in which case it should be as simple as possible and ideally not be a composite) you are mixing OOP and concerns with data mapping concerns; whilst this in and of itself is not such a bad thing, it only serves to increase the complexity of your code.
Consider for example if your incoming JSON ends up with 30 or 40 properties, and you manage to figure out a way (maybe adapting some of the nice techniques from the other answers) to map it to the Entity class. But what about when something goes wrong - it's going to be much easier to reason about, and therefore debug, a process which you have much more control over; it's also going to be much easier to make special adaptations for odd edge cases where the serialiser behaviour just can't help you out
Granted it's a bit of work to write and maintain these DTOs but not that much - Webtools already does this for you
Reference: At the boundaries, Applications are not Object-Oriented

JSON Deserialization Despair (unable to deserialize nested types)

There are probably 500 questions like this on SO, and a million websites out there all offering tidbits of information - but I just can't see the wood for the trees. This seems like it should be embarrassingly simple to do, but I just can't make it work.
I have a WCF webservice that returns a serialized JSON object:
[OperationContract(Name = "PeopleData"), WebGet(BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "people/{subset}", ResponseFormat = WebMessageFormat.Json)]
PeopleObject GetPeople(string subset);
This works - if I hit that URI from a browser, GetPeople is invoked and returns a JSON-serialized PeopleObject (actual data values redacted for privacy here):
{"HashValue":"XXXXX","People":[{"EmailAddress":"XXXXX","EmployeeID":99999,"Gender":"X","JobTitle":"XXXXX","Office":"","PreferredName":"XXXXX","Surname":"XXXXX","WorkExtensionNumber":"XXXXX","WorkPhoneNumber":"XXXXX","Department":"XXXXX","DeskNumber":"XXXXX","EmploymentClassification":"XXXXX","InternationalExtensionNumber":"XXXXX","IsFirstAider":false,"Languages":[{"LanguageID":9,"LanguageSkillID":9},{"LanguageID":9,"LanguageSkillID":9}],"QualificationInitials":"XXXXX","QualificationTitle":"XXXXX","Secretaries":null,"WorkMobilePhoneNumber":"XXXXX"}],"RecordCount":"1","SizeBytes":"12345"}
In this example the PeopleObject payload contains just one Person object in the collection, but could contain many (depending on the parameter supplied in /{subset}.
Here is the class hierarchy for PeopleObject - it's a top-level container holding some metadata about the payload, and a List<> of Person objects. Those objects in turn have a bunch of simple type attributes, plus two further nested List<> of Language and Secretary objects (which may or may not be populated):
[DataContract]
public class PeopleObject
{
[DataMember]
public string HashValue { get; set; }
[DataMember]
public List<Person> People { get; set; }
[DataMember]
public string RecordCount { get; set; }
[DataMember]
public string SizeBytes { get; set; }
}
[DataContract]
public class Person
{
[DataMember]
public string EmailAddress { get; set; }
// <-- snip - lots of fields like this, no point listing them all here
[DataMember]
public bool IsFirstAider { get; set; }
[DataMember]
public List<Language> Languages { get; set; }
[DataMember]
public List<Secretary> Secretaries { get; set; }
}
[DataContract]
public class Language
{
[DataMember]
public int LanguageID { get; set; }
[DataMember]
public int LanguageSkillID { get; set; }
}
[DataContract]
public class Secretary
{
[DataMember]
public int EmployeeID { get; set; }
[DataMember]
public char FirstSurnameLetter { get; set; }
}
So far, so good - WCF responds with a JSON structure that contains all the fields and their contents. Now to deserialize that structure in a client application (using the same class hierarchy definitions):
// I have a little helper-class to manage the WCF request and return a Stream
using (Stream response = wcfHelper.GetRequestResponseStream(MY_WCF_URI))
{
// This is debug code to prove the response arrives as expected - it does
//StreamReader sr = new StreamReader(response);
//Console.WriteLine("\nResponse:\n{0}", sr.ReadToEnd());
// Deserialise the response
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject));
PeopleObject p = (PeopleObject)dc.ReadObject(response);
// The object shows 1 record (in the example) but nothing in the List<>
Console.WriteLine("\nDeserialized records: '{0}' [{1}]", p.RecordCount, p.People.Count);
}
So this correctly deserializes the container object, giving me the record count, hash value, and payload size in bytes. The object does also have a List<> of Person objects, but it's null - the content from the JSON response hasn't successfully rehydrated the List<> by creating and adding a Person object.
What am I missing? My understanding was that this rehydration of the C# object hierarchy from the JSON structure should happen automatically, so either that's not the case (and I need to write some code to make it happen) or it is, but I've missed something obvious.
I haven't done what you are doing before, but judging by the documentation, I'd assume the following would work:
List<Type> types = new List<Type>();
types.Add(typeof(Person));
types.Add(typeof(Language));
types.Add(typeof(Secretary));
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(PeopleObject), types);
PeopleObject p = (PeopleObject)dc.ReadObject(response);
You basically need to tell the Serializer all the types it may encounter while serializing/deserializing your object.

Categories

Resources