Adding Attributes of NFT metadata opensea in C# unity - c#

I am trying to add some data to the attribute parameters of below code in C# but every time it gives me the NullObjectRefrence exception. If anyone know about it please.
public class RootObject
{
public string description;
public string external_url;
public string image;
public string name;
public Attribute[] attributes;
}
[System.Serializable]
public class Attribute
{
public string trait_type;
public string value;
}
In The updatethePlayerData() function below i am trying to add the values of trait_type and its corresponding value.
public void updatethePlayerData()
{
RootObject rootObject = new RootObject();
rootObject.description = "aaa";
rootObject.image = "bbb";
rootObject.external_url = "ccc";
rootObject.name = "dddd";
rootObject.attributes[0].trait_type = "character_class";
rootObject.attributes[0].value = "name of cahracter";
}

Finally, I found the solution.
In the "RootObject" class changed the Attribute array to a list as follows:
public List <Attribute> attributes;
in the calling function create an object of the "Attribute" class as follows:
Attribute attribute = new Attribute();
Now I can assign values to the list as follows:
attribute.trait_type = "Some Value";
Hope this helps if someone looking around!

Related

C#: How to loop through members defined via an array in a class

I have a class as follows :
class Student
{
private string name;
private string family;
private string id;
public string[] fields = {"name", "family", "id"};
public Student(string name,string family,string id)
{
this.name = name;
this.family = family;
this.id = id;
}
}
Now in another class I want to loop through members defined in fields array. This is my code that doesn't work
class StudentController
{
Student student;
public StudentController(Student st)
{
this.student = st;
}
public void store()
{
foreach(string f in this.student.fields)
Console.WriteLine(this.student.f);
}
}
Field name is not a stand-in for field's value, so this.student.f refers to the non-existent field or property called f of your Student object, causing the error.
There are multiple ways of fixing this, depending on your taste. For example, you can use reflection to get field's value (the answer at the link talks about properties; you can adjust it to use fields, or switch to properties, which is a more C#-ish way to do things). An added bonus is that you no longer need to define the list of all fields, unless you want to, because C# lets you get all fields for free.
Alternatively, you could define a collection of Func<Student,string>, where the functor accesses a particular field:
public Dictionary<string,Func<Student,string>> fields = {
["name"] = s => s.name
, ["family"] = s => s.family
, ["id"] = s => s.id
};
Now you can iterate over the fields and print names and values as you go:
foreach(string p in student.fields)
Console.WriteLine("{0} = {1}", p.Key, p.Value(student));
Usually these kind of tasks are resolved using Reflection. The Reflection api system allows you to examine the Type (in this case an instance of a Student) and ask about its properties and methods associated with it. The advantage is that you don't need a public field array of strings and, if you add new members, the code that traverse your properties doesn't change. The downside is the relatively performance impact of using reflection (but to be really concerned you should always test in your real environment)
But you could also implement a simple alternative creating your own version of the ToString method.
For example, extend your Student class to be
public class Student
{
private string name;
private string family;
private string id;
public string[] fields = { "name", "family", "id", "all" };
public Student(string name, string family, string id)
{
this.name = name;
this.family = family;
this.id = id;
}
public string ToString(string field)
{
switch (field)
{
case "name":
return this.name;
case "family":
return this.family;
case "id":
return this.id;
case "all":
default:
return $"{name}, {family}, {id}";
}
}
}
Now you can loop over the fields with
Student t = new Student("John", "McEnroe", "134");
foreach(string s in t.fields)
Console.WriteLine(t.ToString(s));
or just for a single field with
Console.WriteLine(t.ToString("name"));
If you're trying to access the values in
public string[] fields = {"name", "family", "id"};
Then just change this:
foreach(string f in this.student.fields)
Console.WriteLine(this.student.f);
To this:
foreach(string f in this.student.fields)
Console.WriteLine(f);
You were trying to access the variable f as like it was a member of StudentController.
However, if you're trying to access the values of name, family, and id that were passed to the Student constructor, then turn this fields to properties like this:
public string Name { get; set; }
public string Family { get; set; }
public string Id { get; set; }
And use the following code to access them:
foreach(var property in typeof(Student).GetProperties()) {
var propValue = property.GetValue(student);
Console.WriteLine(propValue);
}
If you those properties to only be set from the Student class, then you can either do this:
public string Name { get; }
or this:
public string Name { get; private set; }

Serialization when inheriting from Dictionary

I'm using System.Web.Script.Serialization.JavaScriptSerializer to serialize / deserialize a class that extends Dictionary.
The problem is, my custom properties are not being serialized. Here is my class:
public class Test : Dictionary<string, object> {
public ushort Id { get; set; }
public string Name { get; set; }
}
And my code:
var jss = new JavaScriptSerializer();
var test = new Test {
Id = 123,
Name = "test"
};
var json = jss.Serialize(test);
The result in json is an empty json {}
I do not want to depend on Newtonsoft or JSON.Net or any other library.
ADDITIONAL INFO
I just noticed some, hm, peculiarities, when using both dynamic and object:
JavaScriptSerializer defaults any number value to int.
Also, Newtonsoft defaults any number to long.
That can cause casting exceptions in a class using property indexer (as suggested in the accepted answer), for example:
public class Test : Dictionary<string, dynamic> {
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The Id property getter will try to implicitly convert int to ushort, which will fail.
ADDITIONAL INFO 2
I just found out so many weird behaviors with Newtonsoft:
I added these attributes to solve the 'long to ushort' problem:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
}
The above works! But when the property is a reference type:
[JsonObject(MemberSerialization.OptIn)]
public class Test : Dictionary<string, dynamic> {
[JsonProperty]
public ushort Id { get => this[nameof(Id)]; set => this[nameof(Id)] = value; }
[JsonProperty]
public Test Child { get => this[nameof(Child)]; set => this[nameof(Child)] = value; }
}
It tries to get the property before serializing it, resulting in a 'key not found exception'. I can't see why it tries to get the property only when it's a reference type, seems like a bug to me...
So you have to do something like this:
public Test Child { get => this.ContainsKey(index) ? this[nameof(Child)] : null; ... }
Just to summarize the comments:
MSDN recommends using JSON.NET even on JavaScriptSerializer's own docs
Composition will allow you to use JavaScriptSerializer in this case (instead of inheritance)
To work with existing data structure (inheritance), you would have to implement your own version of JavaScriptObjectDeserializer (https://referencesource.microsoft.com/#system.web.extensions/Script/Serialization/JavaScriptObjectDeserializer.cs,2f8d1f9fbf43dbfa)
The default serializer only supports attribute to ignore attribute (not include/rename)
To use composition, you would just need to modify your test object like this:
public class Test
{
public ushort Id { get; set; }
public string Name { get; set; }
public Dictionary<string, object> Items { get; set; } = new Dictionary<string, object> {};
}
Then the following code would work fine:
var jss = new JavaScriptSerializer();
var test = new Test
{
Id = 123,
Name = "test",
};
test.Items.Add("A", 1);
var json = jss.Serialize(test);
The output would just be:
{"Id":123,"Name":"test","Items":{"A":1}}
UPDATE: Property Indexer
You could add a default indexer to your class so that the following code would work:
test["A"] = 1;
var result = test["A"];
Here is the code to add for the default indexer:
public object this[string key]
{
get { return this.Items[key]; }
set { this.Items[key] = value; }
}
You could extend this into implementing IDictionary I suppose, but I imagine just working with the composition should be easiest.

Deserialize string by class name

Let's say I have a Value that is deserialized from a class.
public class MyValue
{
public string MyPropertyA { get; set; }
public string MyPropertyB { get; set; }
public string DeserializationClass { get; } = typeof(MyValue).Name;
}
I serialize this using JsonConvert class. MyValue class has a property DeserializationClass that should be used as info from which class the string was serialized from. In other words, when I deserialize the string into an object, this property serves as info which class should be used to deserialize the string. However, I am kinda stuck here as I am not sure how to get back the class from the string. Can anybody help me here?
public class Program
{
void Main()
{
var serialized = Serialize();
var obj = Deserialize(serialized);
}
string Serialize()
{
var objValue = new MyValue { MyPropertyA="Something", MyPropertyB="SomethingElse" };
return JsonConvert.SerializeObject<MyClass>(value);
}
object Deserialize(string serialized)
{
//How to deserialize based on 'DeserializationClass' property in serialized string?
return = JsonConvert.Deserialize<???>(serialized);
}
}
EDIT: Modified example to make it more clear what I need as I don't have access to objValue when I need to deserialize the string.
probably you might need to use JsonSerializerSettings.
What you might need to do is
JsonSerializerSettings setting = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
};
and then while serializing use this setting.
var serialized = JsonConvert.SerializeObject(objValue,setting);
this will give you Json like this
{"$type":"WPFDatagrid.MyValue, WPFDatagrid","MyPropertyA":"Something","MyPropertyB":"SomethingElse","DeserializationClass":"MyValue"}
from this you can find the name of the class used it to actually get your type.
Hope this helps !!
There is an overload
If your Type is in form of a Namespace, you can obtain the type from a string representation:
Type objValueType = Type.GetType("Namespace.MyValue, MyAssembly");
object deserialized = JsonConvert.Deserialize(objValueType, serialized);

Dynamic Object's property set

Below is my function :
public static StoreMetaData SetUpdateTime(dynamic myObject)
{
var storeMetaData = new StoreMetaData
{
Created = myObject["StoreMetaData"]["Created"], //Facing problem
Updated = DateTime.Now
};
return storeMetaData;
}
I have three objects :
Teacher
Properties :
public string id{get;set;}
public string name{get;set;}
public StoreMetaData storeMetaData{get;set;}
Student
Properties :
public string id{get;set;}
public string name{get;set;}
public StoreMetaData storeMetaData{get;set;}
StoreMetaData
Properties :
public DateTime? Created { get; set; }
public DateTime? Updated { get; set; }
My problem is how I can get "Created = myObject["StoreMetaData"]["CreatedDate"]".
I want to pass my objects in that function like below:
Part of my code :
teacherObj.StoreMetaData.Created = (currentObject.StoreMetaData != null ? currentObject.StoreMetaData.Created : null);
teacherObj.storeMetaData = SetUpdateTime(teacherObj);
OR,
studentObj.StoreMetaData.Created = (currentObject.StoreMetaData != null ? currentObject.StoreMetaData.Created : null);
studentObj.storeMetaData = SetUpdateTime(studentObj);
At line "Created = myObject["StoreMetaData"]["CreatedDate"]" it shows error. What is the correct format ?
Thanks in advance.
Based purely on the code you posted, your issue is that the storeMetaData property is defined with a lower case first letter, but when you try to access it, you specify it as upper-case.
Try Created = myObject["storeMetaData"]["CreatedDate"];
As an aside, I'm not sure if indexers work like that on dynamic objects. I think they do, but just in case I would use dot syntax as you're dealing with properties.
Created = myObject.storeMetaData.CreatedDate;
Hope that you are passing an object of Teacher or Student so that it contains definition for storeMetaData from that you can access Created which is a property defined inside the StoreMetaData class. Indexers won't help you at this stage, you should use like this:
Created = myObject.StoreMetaData.Created;
Problem solved by me. Thanks every one. :)
Below is my solution :
public static StoreMetaData SetUpdateTime(dynamic myObject)
{
StoreMetaData oStoreMetaData = new StoreMetaData();
oStoreMetaData = myObject.StoreMetaData;
var storeMetaData = new StoreMetaData
{
Created = oStoreMetaData.Created,
Updated = DateTime.Now
};
return storeMetaData;
}

Errors when attempting to dynamically control element names during XML Serialization

I've been researching online, including Stack Overflow, but either I'm missing something or the examples I've seen just don't apply to my situation.
I am receiving this error when I attempt to dynamically set the root and list item element names during XML Serialization.
XmlRoot and XmlType attributes may not be specified for the type
System.Collections.Generic.List`1[
[XmlSerializationFailureExample.Controllers.MyClass, XmlSerializationFailureExample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
I've seen one rather old posting on Microsoft's site which states that the message ought to read: Only XmlRoot and XmlType attributes may be specified.... Sure enough, if I remove all overrides except XmlRoot and XmlType, the error is cleared, but then the rendered XML does not match my needs.
I am using the XmlSerializer Overrides constructor because I have to dynamically set the Root and first child element names. The same classes need to produce different XML element names in different situations. Though this sample has a meager two fields, the actual class being serialized has around 100 fields.
So, how can I control the name of both the root element and the immediate child elements when directly serializing a List<MyClass> object?
The objective is to get XML looking like this:
<ArrayOfPerson>
<Person>
<Name>John Doe</Name>
<Age>57</Age>
</Person>
<Person>
<Name>Doe, Jane</Name>
<Age/>
</Person>
</ArrayOfPerson>
By changing the override values, I should be able to generate XML like this from the same class:
<ArrayOfEmployee>
<Employee>
<Name>John Doe</Name>
<Age>57</Age>
</Employee>
<Employee>
<Name>Doe, Jane</Name>
<Age/>
</Employee>
</ArrayOfEmployee>
Here is some simplified code that demonstrates my problem. I used a basic MVC.Net app from Visual Studio 2013's templates for this example.
// GET api/SerializationTest
public ActionResult SerializationTest()
{
var list = new List<MyClass> {
new MyClass {Name = "John Doe", Age = 57},
new MyClass {Name = "Doe, Jane"}
};
XmlAttributes xmlPerson = new XmlAttributes {
XmlRoot = new XmlRootAttribute { ElementName = "Person" }
};
XmlAttributes xmlPersonList = new XmlAttributes {
XmlRoot = new XmlRootAttribute { ElementName = "ArrayOfPerson" },
XmlArrayItems = {
new XmlArrayItemAttribute("Person",typeof(MyClass))
},
};
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(MyClass), xmlPerson);
overrides.Add(typeof(List<MyClass>), xmlPersonList);
return new XmlResult(
list,
"TestFile.xml",
overrides
);
}
The example class being serialized. The actual class has around 100 properties.
public class MyClass
{
public string Name { get; set; }
public int? Age { get; set; }
}
Update 1
If I encapsulate my List<MyClass> in another class and annotate with attributes as below, I can get the XML I want. But how do I do this dynamically, as the specified element names must vary at runtime?
[XmlRoot(ElementName = "ArrayOfPerson")]
public class MyCollection
{
[XmlElement(ElementName = "Person")]
public List<MyClass> Items { get; set; }
}
End of Update 1
The XmlResult type derives from the built-in ActionResult and contains the actual serialization logic. The class is intended to return a file rather than an HTML page.
public class XmlResult : ActionResult
{
private string FileName { get; set; }
private object ObjectToSerialize { get; set; }
private XmlAttributeOverrides Overrides { get; set; }
public XmlResult(object objectToSerialize, string fileName, XmlAttributeOverrides overrides)
{
ObjectToSerialize = objectToSerialize;
FileName = fileName;
Overrides = overrides;
}
public override void ExecuteResult(ControllerContext context)
{
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.AddHeader("content-control", "no-store, no-cache");
HttpContext.Current.Response.AddHeader("content-disposition", "attachment;filename=" + FileName);
HttpContext.Current.Response.ContentType = "text/xml";
try
{
if (ObjectToSerialize != null)
{
var xs = new XmlSerializer(ObjectToSerialize.GetType(), Overrides);
xs.Serialize(HttpContext.Current.Response.Output, ObjectToSerialize);
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("<error>" + ex + "</error>");
}
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.SuppressContent = true;
HttpContext.Current.ApplicationInstance.CompleteRequest();
}
}
Well, I was not able to make things as dynamic as I wanted, but this solution seems to work.
First, I made my class an abstract base class:
public abstract class MyBaseClass
{
public string Name { get; set; }
public int? Age { get; set; }
}
Then I created derived classes for each of the XML element names I wanted:
public class Person : MyBaseClass { }
public class Employee : MyBaseClass { }
Then it was a simple matter of making sure I used the correct derived classes within my controller action methods.
public ActionResult SerializePersonCollectionByXml()
{
var list = new List<Person> {
new Person {Name = "John Doe", Age = 57},
new Person {Name = "Doe, Jane"}
};
return new XmlResult(
list,
"PersonCollectionByXml.xml"
);
}
// GET api/SerializationTest
public ActionResult SerializeEmployeeCollectionByXml()
{
var list = new List<Employee> {
new Employee {Name = "John Doe", Age = 57},
new Employee {Name = "Doe, Jane"}
};
return new XmlResult(
list,
"EmployeeCollectionByXml.xml"
);
}
Of course, things were a bit more complicated in the real application...
The technique above allowed me to keep all the properties and methods in the base class and just use the derived class to get the desired XML element names.
The real app was choosing the Person or Employee class at run-time, based upon an input parameter to the action method. Further, the logic was buried within another set of classes with an inheritance structure mirroring the classes. Since .Net 4.6 does not yet support covariance with generic types (only interfaces and delegates), I had to go through a few more gyrations to get the right return values from the inner logic before I could return them via the XmlResult class.
Example:
Internal Service Classes:
public abstract class ServiceBase<TRecord> : IDisposable where TRecord : MyBaseClass
{
public abstract List<TRecord> Search(SearchParams p);
}
public class ServicePerson : ServiceBase<Person>
{
public override List<Person> Search(SearchParams p)
{
var result = base.Search(p);
// for example only, just used a simple cast; more complex operation may be required.
return result.Select(r => (Person)r).ToList();
}
}
public class ServiceEmployee : ServiceBase<Employee>
{
public override List<Employee> Search(SearchParams p)
{
var result = base.Search(p);
// for example only, just used a simple cast; more complex operation may be required.
return result.Select(r => (Employee)r).ToList();
}
}
Controller's action method:
public ActionResult Search(Guid apiKey, SearchParams p)
{
try
{
if (p.UsePerson)
{
using (var service = new ServicePerson())
{
List<Person> result = service.Search(p);
return XmlResult(result, "PersonList.xml");
}
}
using (var service = new ServiceEmployee())
{
List<Employee> result = service.Search(p);
return XmlResult(result, "EmployeeList.xml");
}
}
catch (Exception ex)
{
// log error
}
}

Categories

Resources