Looks like no C# JSON serialization framework can deserialize self reference loop.
But why?
Let's imagine that we have this class structure:
public class Person
{
public string Name { get; set; };
public City City { get; set; }
}
public class City
{
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
and in serialization/deserialization settings we say that if objects are the same put $ref instead of serializing object again
and serialized person will look like this
{
"$id":"1",
"Name":"John",
"City":{
"$id":"2",
"Name":"London",
"Persons":[
{
"$ref":"1"
}
]
and on deserialization you are simply creating person with no City then creating City with person and initialize person property with created City
var person = new Person();
var city = new City();
city.Persons = new List<Person>{ person };
person.City = city;
One explanation I can find was that if you have no empty constructors, you need to create parameters first.
But even if you have one, all serializing frameworks that I can find cannot deserialize it properly.
EDIT:
IT CAN BE DESERIALIZED.
I thought that structure in the example is according to my real class structure, my real structure was not deserializing properly, so I didn't try to really serialize/deserialize structure from the example. Then, when I tried it was deserialized fine, so I checked my real structure and the reason why it was not deserializing properly is that I missed setter on property.
You didn’t show any actual attempts to serialize/deserialize so I don’t know what you’ve tried, but it took me 30s to find out how to do it with System.Text.Json so I suggest you give that a try. It involves configuring the ReferenceHandler option in JsonSerializerOptions
JsonSerializerOptions options = new()
{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};
Microsoft page devoted to this topic
Related
I have two objects with the same type and values
how can I compare them by value?
exp:
class Person
{
public string Name { get; set; }
public DateTime BirthDate { get; set; }
public Address Address { get; set; }
}
class Address
{
public string City { get; set; }
public int ZipCode { get; set; }
}
var p1 = new Person()
{
Name = "John doe",
BirthDate = new DateTime(2000, 1, 1),
Address = new Address(){
City = "some city",
ZipCode = 123456
}
};
var p2 = new Person()
{
Name = "John doe",
BirthDate = new DateTime(2000, 1, 1),
Address = new Address(){
City = "some city",
ZipCode = 123456
}
};
so how can I compare these objects with value?
Mabey in future I wanna change my objects so I need a general way that not depends on object properties names and types
use json
Convert each object into a json string, with all properties/fields sorted by name, then compare two strings. this is slow, but it works.
use reflection
Using reflection methods, compare each property/field one by one. Actually the json library do the same job. Doing the comparison yourself will save the time converting to string, but you have to dive into the nested types.
use some code generator, e.g. protobuf
If the datatype is just POCO type, maybe protobuf is a good choice. It introduces a lot advantages:
build-in comparison
json serialization and deserialization
very fast binary serialization and deserialization
cross-platform and cross language, integrated well with grpc inter-process communication
version compatibility, when new fields added, old data on disk can still be read by app.
just make an "Equal function" in your Person class
public bool class Equals(Person source)
{
if(this.Name!=source.Name) return false;
if(this.Surname!=source.Surname)return false;
....
return true;
}
Then use like that :
if(myFirstPerson.Equals(mySecondPerson))
{
}
Like that you can place as many as attributes as you want, then even make several Equals functions, if you need not to always compare the same attributes. Personally I always use this way (instead of "if values equal", I put "if value not equal then return false"), cause very useful when you have a lot of values to compare, and different equal functions.
I have a client that sends SomeComplexObject to a webservice.
I want the webservice to be unaware of the structure of the data,
so want the data to be deserialized to a dynamic that is passed then to a method that knows how to deal with it.
I use System.Web.Helpers.Json.Encode(), Decode() methods
and I have a problem when SomeComplexObject contains a collection.
It is deserialized to a DynamicJsonArray but it is somehow not accessible
for the consumer of the data.
These are Model types.
public class Aaa
{
public Bbb B { get; set; }
public List<Ccc> Cccs { get; set; }
}
public class Bbb
{
public long Key { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Ccc
{
public string Name { get; set; }
public int Age { get; set; }
}
Let us say that I have a myAaa object of type Aaa with a property of type Bbb
and a list of 42 Ccc objects.
Now:
var MyAaaSerialized = System.Web.Helpers.Json.Encode(MyAaa);
Then I send it and then:
var MyAaaDeserialized = System.Web.Helpers.Json.Decode(MyAaaSerialized);
And finally:
This is the code of the consumer. Email and emailService are Postal classes.
dynamic email = new Email(template);
email.Data = MyAaaDeserialized;
email.User = user;
this.emailService.Send(email);
I can see in the sent email that properties of B object are accessible.
However, properties of members of Cccs list are not.
Of course with email.Data = MyAaa; everything works OK.
Is there any dead simple way to serialize/deserialize a complex object that contains a collection?
Thanks in advance,
If I haven't misunderstood your question, it can be resolved by using the Decode overload that uses generics as below. Just specifying the type (Aaa in this case) worked for me in the code below and it printed all ccc properties correctly:
var myAaa = new Aaa()
{
B = new Bbb() { Name = "someone", DateOfBirth = DateTime.Today.AddYears(-20) },
Cccs = Enumerable.Repeat<Ccc>(new Ccc() { Age = 20, Name = "someone else" }, 42).ToList()
};
var MyAaaSerialized = System.Web.Helpers.Json.Encode(myAaa);
var MyAaaDeserialized = System.Web.Helpers.Json.Decode<Aaa>(MyAaaSerialized);
dynamic data = MyAaaDeserialized;
foreach (Ccc newCccs in data.Cccs)
{
Console.WriteLine($"{newCccs.Name}\t{newCccs.Age}");
}
Ok, I was wrong. System.Web.Helpers.Json.Encode() and Decode() work correctly. I`ll tell you what happened:
I used to use Postal mailsender locally, i.e. I used to take my super-complex POCO object and pass it as a dynamic to Postal Email class. And it worked.
Now I want some webservice to send emails so I need to pass serialized data to it. I have sent a test email and I`ve seen that values of some of the fields are not present in the email message, but 'DynamicJsonObject' is there instead. So I assumed (wrong), that the structure of deserialized object is somehow corrupted.
But the truth is different:
The missing data is of complex type, say
public class Money
{
public double Amount {get; set;}
public string Currency {get; set;}
public override ToString()
{...}
}
So when I asked in Razor email template for:
#item.Prices.SalePriceGross locally,
it somehow must have used the ToString() method.
And of course in a serialized / deserialized object there is no knowledge about the ToString() method.
I am going to need to expose a property with the string I want to display or (better) access Amount and Currency explicitly and process them in Razor email template.
Thank you for help.
There are a number of great ways to auto-generate C# code from JSON, such as here and here.
However, the resulting code doesn't include property initializers. For example, the following JSON:
{
"Name" : "Blastoise"
}
gets deserialized to this:
public class RootObject
{
public string Name { get; set; }
}
Presumably this is by design, since the values used in the JSON will probably be overridden anyways, so adding initializers might just annoy people who don't want them.
But what if I want that? Short of manually adding every value by hand, is there a way to deserialize JSON to the following?
public class RootObject
{
public string Name { get; set; } = "Blastoise";
}
Obviously in this case a manual edit is easy, but manual editing becomes tedious for larger JSON objects.
is there a way to deserialize JSON to the following?
Using the source code of the converter you mentioned.
A quick change at the line 204
sw.WriteLine(prefix + "public {0} {1} {{ get; set; }} = {2};", field.Type.GetTypeName(), field.MemberName, field.GetExamplesText());
gives me the result similar to what you described
internal class SampleResponse1
{
[JsonProperty("Name")]
public string Name { get; set; } = "Blastoise";
}
I have a simple POCO for person object that looks like this:
public class Person
{
public int PersonId {get; set;}
public string FirstName {get; set;}
public string MiddleName {get; set;}
public string LastName {get; set;}
public char Gender {get; set;}
}
I have the following code that queries the People collection in my DocumentDb database.
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
dynamic doc = client.CreateDocumentQuery<Document>(colSelfLink, "SELECT p.PersonId, p.FirstName, p.MiddleName, p.LastName, p.Gender FROM People p").AsEnumerable().FirstOrDefault();
List<Person> peopleList = new List<Person>();
if (doc != null)
{
peopleList = doc;
}
return peopleList;
}
When I run this code, I'm getting the following error:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyApp.Person]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'PersonId', line 1, position
48.
How do I convert the JSON object coming from my query to a Person object? In my MVC app, the model binder does a pretty good job but this is running in a separate class library. How should I approach converting this JSON object?
This is what I did and it works. I'd appreciate other answers though if there's a better way.
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
dynamic doc = client.CreateDocumentQuery<Document>(colSelfLink, "SELECT p.PersonId, p.FirstName, p.MiddleName, p.LastName, p.Gender FROM People p").AsEnumerable().ToList();
List<Person> peopleList = new List<Person>();
if (doc != null)
{
Person person;
foreach(var item in doc)
{
person = JsonConvert.DeserializeObject<Person>(item.ToString());
peopleList.Add(person);
}
}
return peopleList;
}
Anthony Chu was on the right track with this one. The simple solution is:
private async static Task<List<Person>> GetPeopleList(string colSelfLink)
{
return client.CreateDocumentQuery<Person>(colSelfLink, "SELECT * FROM People").ToList();
}
This will automatically deserialise each returned record and turn it into a People object.
I had this exact same problem, only that my list of objects failing, was nested in another object (document).
public List<MyObject> MyProperty { get; set; }
I am so happy right now that I found the solution:
If you have lists of complex objects in your document, put this on the properties of the complex object:
[JsonProperty(PropertyName = "yourPropertyName")]
This is well known to be needed on the primary object (document), haven't needed them on nested objects though, until they were in a list.
This is a very complicated question concerning how to serialize data via a web service call, when the data is not-strongly typed. I'll try to lay it out as best possible.
Sample Storage Object:
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<NameValuePairs> OtherInfo { get; set; }
}
[Serializable]
public class NameValuePairs {
public string Name { get; set; }
public string Value { get; set; }
}
Sample Use:
[WebMethod]
public List<StorageObject> GetStorageObjects() {
List<StorageObject> o = new List<StorageObject>() {
new StorageObject() {
Name = "Matthew",
Birthday = "Jan 1st, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
},
new StorageObject() {
Name = "Joe",
Birthday = "Jan 10th, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
}
};
return o;
}
Return Value from Web Service:
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<NameValuePairs>
<Name>Hobbies</Name>
<Value>Programming</Value>
</NameValuePairs>
<NameValuePairs>
<Name>Website</Name>
<Value>Stackoverflow.com</Value>
</NameValuePairs>
</OtherInfo>
</StorageObject>
What I want:
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
The Reason & Other Stuff:
First, I'm sorry for the length of the post, but I wanted to give reproducible code as well.
I want it in this format, because I'm consuming the web services from PHP. I want to easily go:
// THIS IS IMPORANT
In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".
If it's in the other format, then there would be no way for me to accomplish that, at all. Additionally, in C# if I am consuming the service, I would also like to be able to do the following:
// THIS IS IMPORANT
In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];
Unfortunately, I'm not sure how to accomplish this. I was able to get it this way, by building a custom Dictionary that implemented IXmlSerializer (see StackOverflow: IXmlSerializer Dictionary), however, it blew the WSDL schema out of the water. It's also much too complicated, and produced horrible results in my WinFormsTester application!
Is there any way to accomplish this ? What type of objects do I need to create ? Is there any way to do this /other than by making a strongly typed collection/ ? Obviously, if I make it strongly typed like this:
public class OtherInfo {
public string Hobbies { get; set; }
public string FavoriteWebsite { get; set; }
}
Then it would work perfectly, I would have no WSDL issues, I would be able to easily access it from PHP, and C# (.OtherInfo.Hobbies).
However, I would completely lose the point of NVP's, in that I would have to know in advance what the list is, and it would be unchangeable.. say, from a Database.
Thanks everyone!! I hope we're able to come up with some sort of solution to this. Here's are the requirements again:
WSDL schema should not break
Name value pairs (NVP's) should be serialized into attribute format
Should be easy to access NVP's in PHP by name ["Hobbies"]
Should be easy to access in C# (and be compatible with it's Proxy generator)
Be easily serializable
Not require me to strongly type the data
Now, I am /completely/ open to input on a better/different way to do this. I'm storing some relatively "static" information (like Name), and a bunch of pieces of data. If there's a better way, I'd love to hear it.
This is like dynamic properties for a object.
C# is not quite a dynamic language unlike javascript or maybe PHP can parse the object properties on the fly. The following two methods are what I can think of. The second one might fit into your requirements.
The KISS Way
The Keep It Simple Stupid way
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<string> OtherInfo { get; set; }
}
You can have name value pairs which is separated by '|'
OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}
Serialized forms
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<string>Hobbies|Programming</string>
<string>Website|Stackoverflow.com</string>
</OtherInfo>
</StorageObject>
The Dynamic Way in C#
Make the name value pair part become an XML element so that you can build it dynamically.
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}
You can easily build up OtherInfo object as element centric
e.g.
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);
The serialized form will be
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
or build it as attribute centric
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);
<OtherInfo>
<nvp n="Hobbies" v="Programming" />
<nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>
For any dynamic language, it can access to the properties directly.
For the rest, they can access the value by read the XML. Reading XML is well supported by most of framework.
This is what I've settled on.
Class Structure:
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[XmlAnyElement("Info")] // this prevents double-nodes in the XML
public XElement OtherInfo { get; set; }
}
Usage:
StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");
Output:
<Info>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</Info>
I would like to thank everyone for their assistance, I really appreciate the help and ideas.
As a completely different take on this, why not think about doing it completely differently. Have one web service method to return the serialized storage object, minus the OtherInfo and another method to return the list of properties (keys) for OtherInfo, and a third to return the list of values for any key. Granted, it will take more round trips to the web service if you want all of the data, but the solution will be much simpler and more flexible.
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[Nonserializable]
public Dictionary<string,List<string>> OtherInfo { get; set; }
}
[WebMethod]
public List<StorageObject> GetStorageObjects() {
// returns list of storage objects from persistent storage or cache
}
[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
// find storage object, sObj
return sObj.Keys.ToList();
}
[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
// find storage object, sObj
return sObj[attribute];
}
Have a look into the System.Xml.Serialization.XmlSerializerAssemblyAttribute attribute. This lets you specify a custom class-level serializer. You'll be able to spit out whatever XML you like.
A quick way to get up to speed on these is to use sgen.exe to generate one and have a peek at it with Reflector.
-Oisin
I'm not sure this would solve your problem (it would in C#, but maybe not in PHP), but try using Dictionary<string,List<string>> OtherInfo instead of List<NameValuePairs>. Then "Hobbies" and "Websites" would be your keys and the values would be the list of hobbies or web sites. I'm not sure how it would serialize, though.
You would be able to reference the lists of hobbies as:
List<string> hobbies = storageObject.OtherInfo["Hobbies"];
[EDIT] See here for a generic XML serializable dictionary. This derived class is the one you would need to use instead of generic Dictionary.