How to set values of object array [duplicate] - c#

This question already has answers here:
Elegant initialization of an array of class instances in C# [closed]
(3 answers)
Constructor for an array of class objects
(2 answers)
Closed 1 year ago.
Hi I have this code not sure how I can set the value of Value and Value1
public class testclass {
public ID[] test { get; set; }
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
}
}
e.g. var a = new testclass ();
a.test = ????

The fastest way with your existing code is:
a.test = new testclass.ID[] { new testclass.ID() { Value = "test", Value1 = "test1" } };
But some refactoring might help you a bit. Here's a fully compiling example.
Constructors were added to make the syntax cleaner. Rather than using the syntax of new ID { Value = "", Value1 = "" }; you can use new ID("", "");. Note that since you're doing serialization/deserialization, you may need to explicitly include a parameterless constructor, as it's unclear from your small code sample how this might get handled.
And the ID class was separated out, no longer 'underneath' the testclass class.
using System.Text.Json.Serialization; // from System.Text.Json library
namespace SomeNamespace
{
public class Program
{
static void Main()
{
var id = new ID("hello", "world");
var a = new testclass(id);
}
}
public class testclass
{
public ID[] test { get; set; }
public testclass() { /* might need parameterless for serialization */ }
// just makes an array with one entry
public testclass(ID id)
{
test = new ID[] { id };
}
}
// separate ID class from 'testclass'
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
public ID() { /* might need parameterless for serialization */ }
public ID(string value, string value1)
{
Value = value;
Value1 = value1;
}
}
}

This is what you're looking for:
var a = new testclass ();
a.test = new testclass.ID[]{};
You can also use object initialization like this:
a.test = new testclass.ID[]{
new testclass.ID(){Value = "1", Value1 = "2"},
new testclass.ID(){Value = "AnotherValue", Value1 = "10"}
};
And to read individual values:
a.test[0].Value == "1"
a.test[1].Value1 == "10"
Though these kind of nested classes are not recommended for most cases, it should work.

Related

Deserialize JSON into dynamic class

I need to deserialize a JSON string into a type which is not know at compile time. There are several classes that it can be deserialized into. The name of the class is provided as input into the application and based on that I want to instantiate the class (already done this through reflection):
var type = Type.GetType(className);
var myClassInstance = (IParser)Activator.CreateInstance(type);
...and then use its type as the generic type parameter for JsonConvert.DeserializeObject<typeof(myClassInstance).Name>(jsonString) but that doesn't work.
How can I provide the class to DeserializeObject<>() dynamically?
Instead of using an generic method overload like JsonConvert.DeserializeObject<T>(String) and having to resort to reflection as some comments state, you could simply use the non generic counterpart JsonConvert.DeserializeObject(String, Type), which just takes in a Type instance like you already have!
Implementation
Initialization
var class1s = new Class1() {
ID = 1, Name = "Test", Comment = "This Code is Tested!."
};
var class2s = new Class2() {
xVal1 = 1, XVal2 = 5, xval3 = 10
};
var JSON1 = Newtonsoft.Json.JsonConvert.SerializeObject(class1s);
var JSON2 = Newtonsoft.Json.JsonConvert.SerializeObject(class2s);
Calling Functions
var classname1 = typeof(Class1).FullName;
var type1 = Type.GetType(classname1);
var classname2 = typeof(Class2).FullName;
var type2 = Type.GetType(classname2);
var c = LocalConverter(JSON1, type1);
var c2 = LocalConverter(JSON2, type2);
Class Models
public class Class1 {
public int ID {
get;
set;
}
public string Name {
get;
set;
}
public string Comment {
get;
set;
}
}
public class Class2 {
public int xVal1 {
get;
set;
}
public int XVal2 {
get;
set;
}
public int xval3 {
get;
set;
}
}
Required Method
private object LocalConverter(string o, Type xtype) {
return Newtonsoft.Json.JsonConvert.DeserializeObject(o, xtype);
}

How can I compare two lists with xunit test

I am currently trying to compare two lists, with the same items in it, with xUnit but getting an error while running.
Assert.Equal(expectedList, actualList);
Error:
"Assert.Equal() Failure"
Expected: List<myObject> [myObject { modifier = '+', name = "name", type = "string" }, myObject { modifier = '+', name = "age", type = "int" }]
Actual: List<myObject> [myObject { modifier = '+', name = "name", type = "string" }, myObject { modifier = '+', name = "age", type = "int" }]
This has to do with object equality.
MyObject does not implement the Equals method. By default you get a reference equality. I assume you have two different objects for MyObject.
Meaning it does not matter that your List holds the similar object(meaning with same values) they are not of the same reference, so your test checks that, this is why it fails.
internal class MyObject
{
{
public char Modifier { get; set; }
public string Name { get; set; }
public string Type { get; set; }
}
}
[Fact]
public void ListMyObject()
{
var list1 = new List<MyObject>
{
new MyObject{ }
};
var list2 = new List<MyObject>
{
new MyObject{ }
};
Assert.Equal(list1, list2); // Fails
}
When we update our class to this.
internal class MyObject
{
public char Modifier { get; set; }
public string Name { get; set; }
public string Type { get; set; }
//When i add this to my class.
public override bool Equals(object obj)
{
return this.Name == ((MyObject)obj).Name;
}
}
Also as mentioned in the comments by Jonathon Chase.
It is a good idea to override the GetHashCode() method as well. It is preferred to inherit from IEquatable<T> so you can avoid casting.
Everything goes green.
[Fact]
public void ListMyObject()
{
var list1 = new List<MyObject>
{
new MyObject{ Name = "H" }
};
var list2 = new List<MyObject>
{
new MyObject{ Name = "H" }
};
Assert.Equal(list1, list2); //Passes
}

Compare 2 Properties of equals object

I have a class with 2 properties
public class SampleClass
{
public string Name { get; set; }
public List<Component> Components { get; set; }
}
And another class which is hold some string properties.
public class Component
{
public string Name { get; set; }
public string Age{ get; set; }
}
I have instance of this class created and added into a List
SampleClass classWithValues = new SampleClass();
var listComponent = new List<Component>();
listComponent.add(new Component{Name = "Random string",Age = "31"})
classWithValues.Components = listComponent;
classWithValues.Name = "TestName"
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.add(classWithValues);
Then i made a new instance of the SampleClass class and add exactly the same value into the properties :
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new List<Component>();
listComponent1.add(new Component{Name = "Random string",Age = "31"})
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
And here is coming the strange part :
if I compare the property Names inside the list with the second instance of the Sample class with the new instance of the same class:
bool alreadyExists = listWithObjectClass.Any(x => x.Name == classWithValues1 .Name);
the result is true BUT
if I compare the List properties
bool alreadyExists = listWithObjectClass.Any(x => x.Components == classWithValues1.Components);
the result is false ?!
Can someone please give some information about this behavior.
Sorry my first answer was not quite right...
In order to get alreadyExist to be true you need to put in place property comparison in your classes as otherwise the equality comparison performed is the default reference comparison. Your objects contains the same property values but are actually different instances... The default equality comparison for objects is comparing references not content.
Try this...
void Main()
{
SampleClass classWithValues = new SampleClass();
var listComponent = new Components();
listComponent.Add(new Component{Name = "Random string",Age = "31"});
classWithValues.Components = listComponent;
classWithValues.Name = "TestName";
var listWithObjectClass = new List<SampleClass>();
listWithObjectClass.Add(classWithValues);
SampleClass classWithValues1 = new SampleClass();
var listComponent1 = new Components();
listComponent1.Add(new Component{Name = "Random string",Age = "31"});
classWithValues1.Components = listComponent1;
classWithValues1.Name = "TestName";
bool alreadyExists = listWithObjectClass.Any(x => x.Components.Equals(classWithValues1.Components));
}
public class SampleClass
{
public string Name { get; set; }
public Components Components { get; set; }
}
public class Component : IEquatable<Component>
{
public string Name { get; set; }
public string Age{ get; set; }
public bool Equals(Component otherComponent)
{
return Name == otherComponent.Name && Age == otherComponent.Age;
}
}
public class Components :List<Component>, IEquatable<Components>
{
public bool Equals(Components otherComponents)
{
if(this.Count!= otherComponents.Count) return false;
return this.TrueForAll(a=> otherComponents.Any(q=>q.Equals(a)))
&& otherComponents.TrueForAll(a=> this.Any(q=>q.Equals(a)));
}
}
The first comparison is about comparing the value of the two string. However, the second comparison is about Comparing two different object which their reference are different. Indeed, for the second comparison, compare their hashCode. To watch this, you can call .GetHashCode() for these two objects.
listComponent.GetHashCode() == listComponent1.GetHashCode() // false
listComponent[0].GetHashCode() == listComponent1[0].GetHashCode() // false

How to remove DataContractSerializer verbosity

We are attempting to serialize an object tree. And while we have been successful. I was hoping to find a way to simplify down the generated xml.
The objects look something like the following:
public class RuleSet<T>
{
public IEnumerable<IState<T>> States { get; set; }
public IEnumerable<ICondition<T>> Conditions { get; set; }
}
public class State<T> : IState<T>
{
public string Id { get; set; }
public List<ITransition<T>> Transitions { get; set; }
}
public class Transition<T> : ITransition<T>
{
public ICondition<T> Condition { get; set; }
public IState<T> Next { get; set; }
}
public class Condition<T> : ICondition<T>
{
public string Id { get; set; }
public string Name { get; set; }
}
We are using a really simple serialization code at the moment:
public void blah()
{
var condition1 = new Condition<object>() {
Id = "C1", AttributeName = "Foo", ExpectedValue = "Bar"
};
var condition2 = new Condition<object>() {
Id = "C2", AttributeName = "Bar", ExpectedValue = "Foo"
};
var state1Transitions = new List<ITransition<object>>();
var state2Transitions = new List<ITransition<object>>();
var state3Transitions = new List<ITransition<object>>();
var state = new State<object> {
Id = "S1", Transitions = state1Transitions
};
var state2 = new State<object> {
Id = "S2", Transitions = state2Transitions
};
var state3 = new State<object> {
Id = "S3", Transitions = state3Transitions
};
state1Transitions.Add(new Transition<object> {
Condition = condition1, Next = state2
});
state1Transitions.Add(new Transition<object> {
Condition = condition2, Next = state3
});
state2Transitions.Add(new Transition<object> {
Condition = condition2, Next = state3
});
var ruleSet = new RuleSet<object> {
States = new List<IState<object>> {state, state2, state3},
Conditions = new List<ICondition<object>>{condition1, condition2}
};
var stream1 = new MemoryStream();
var serializer = new DataContractSerializer(typeof(RuleSet<object>),
new List<Type> {
typeof(State<object>),
typeof(Transition<object>),
typeof(AttributeEqualTo<object>)
});
serializer.WriteObject(stream1, ruleSet);
stream1.Position = 0;
var xml = new StreamReader(stream1).ReadToEnd();
Console.WriteLine(xml);
}
When the XML is generated the output for each level is complete instead of only containing the reference to the object. Basically for each Transition<T> we get a complete object definition for each state and condition even if they are defined elsewhere.
Is there a way to get those to simply be references?
It all comes down to how you create your DataContractSerializer.
You'll want to call the overload of the constructor that allows you to indicate that you will preserve object references, with the following signature:
public DataContractSerializer(
Type type,
IEnumerable<Type> knownTypes,
int maxItemsInObjectGraph,
bool ignoreExtensionDataObject,
bool preserveObjectReferences,
IDataContractSurrogate dataContractSurrogate
)
You can pass default values for most of the parameters. In your case, the call to the DataContractSerializer constructor will look like this:
var serializer = new DataContractSerializer(typeof(RuleSet<object>),
new [] {
typeof(State<object>),
typeof(Transition<object>),
typeof(AttributeEqualTo<object>)
},
Int32.MaxValue,
false,
/* This is the important flag to set. */
true,
null
);
Note, from the preserveObjectReferences parameter documentation, it uses non-standard XML (emphasis mine):
preserveObjectReferences
Type: System.Boolean
true to use non-standard XML constructs to preserve object reference data; otherwise, false.
If you need other code outside of .NET to interpret this, then untangling the references might prove to be difficult (but shouldn't be impossible).
However, it prevents the object graph from replicating itself over and reduce the size of your XML (possibly considerably, given how deep your references go).

Casting/unboxing generic types

Good day,
I have a class that is used to store a value of Type T that I don't know what the type will be until runtime. I want to unbox/cast, not sure what the correct term is, a specific type (in this case a nullable decimal) back to type object.
Please forgive my code layout:
The class snippet:
public abstract class Types
{
public class ValueField<T>
{
[XmlAttribute]
public int TemplateID { get; set; }
[XmlAttribute]
public int FieldID { get; set; }
[XmlIgnore]
[ScriptIgnore]
public TemplateApprovalField Field { get; set; }
[XmlIgnore]
[ScriptIgnore]
public InstanceTemplateActivityValues Values { get; set; }
[XmlAttribute]
public T Value { get; set; }
}
}
The function snippet:
I am stuck at the line "values.Add((Types.ValueField)field);", don't know how to cast it. At that moment, var field is a Types.ValueField.
Values = new Func<XmlDocument, List<Types.ValueField<object>>>(xml =>
{
List<Types.ValueField<object>> values = new List<Types.ValueField<object>>();
if (xml != null)
{
foreach (XmlNode node in xml.SelectNodes("//Field"))
{
if (node.Attributes["Type"].Value == "Numeric")
{
var field = new Types.ValueField<decimal?>()
{
Field = ApprovalFields.Find(f => f.FieldID == int.Parse(node.Attributes["ID"].Value)),
FieldID = int.Parse(node.Attributes["ID"].Value),
TemplateID = int.Parse(node.SelectSingleNode("../#ID").Value)
};
field.Value = new Func<string, decimal?>(val =>
{
if (string.IsNullOrEmpty(val))
return null;
else
{
decimal parsed = 0;
if (decimal.TryParse(val, out parsed))
return parsed;
}
return null;
})(node.InnerText);
values.Add((Types.ValueField<object>)field); //This is where my problem occurs...
}
}
}
return values;
})(row["Form_Values"] != DBNull.Value ?
new XmlDocument() { InnerXml = row["Form_Values"].ToString() } : null)
Any input will be greatly appreciated.
Regards
YP
You're creating a ValueField<decimal?>. That isn't a ValueField<object> - your ValueField<T> class isn't covariant in T, and couldn't be. (Classes can't be covariant, and your API includes T in "in" positions too.)
To demonstrate why this mustn't work:
Value<decimal?> foo = new Value<decimal?>();
Value<object> bar = foo; // Imagine this worked
bar.Value = "hello";
decimal? x = foo.Value;
What would you expect that to do? Everything other than the second line is above reproach, so we must make the second line fail, which it does.
The simple answer here is to create a Value<object> in the first place, instead of a Value<decimal?>.

Categories

Resources