I have a class with a dynamic member. When the value is a boolean, it's serialized as true / false which deserializes to a string. Is this an issue with YamlDotNet, or if not, how can I force it to serialize as true rather than "true"?
This is a class I'm trying to serialize
public class Field : IField
{
public string MachineName { get; set; }
public string DisplayName { get; set; }
public FieldFormatterEnum FormatterType { get; set; }
public dynamic Value { get; set; }
public dynamic DefaultValue { get; set; }
public dynamic FormattedValue { get; set; }
...
}
The typed deserializer method:
public T DeserializeObject<T>(string input)
{
T o;
using (var tr = new StringReader(input))
{
o = new Deserializer().Deserialize<T>(tr);
}
return o;
}
Field is in a List in another class which is being serialized / deserialized.
Update: After looking at the serialized output, YamlDotNet outputs the serialized boolean as true and not "true". So.. I'm guessing it's a problem with the deserialization code in YamlDotNet?
For serialization, you could create a getter-only property as a bool that has the value you want:
public bool ValueAsBool
{
get
{
bool outValue = false;
if(Value is string)
{
bool.TryParse((string)Value, out outValue);
return outValue;
}
return outValue;
}
}
Ultimately, when working with dynamics you will be throwing some type information away, and you shouldn't expect to be able to recover all of it properly.
I've just set up a simple test like:
class Foo
{
public dynamic V { get; set; }
}
var f1 = new Foo(){V = 5};
var tw2 = new StringWriter();
new Serializer().Serialize(tw2, f1);
var tr2 = new StringReader(tw2.ToString());
var f_1 = new Deserializer().Deserialize<Foo>(tr2);
... and V is deserialized as "5". The default type to deserialize to is string
Related
How to serialize a generic(T) object which can hold any type of data (int / string / DateTime) using Protobuf-net. Following is my code
[ProtoContract]
public class E1DataRow
{
[ProtoMember(1)]
public List<NameValue> NameValues { get; set; }
public E1DataRow()
{
NameValues = new List<NameValue>();
}
public void AddNameValue(string name, object obj, Type type)
{
NameValues.Add(new NameValue { Name = name, Value = obj, ValueType = type });
}
}
[ProtoContract]
public class NameValue
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public object Value { get; set; }
[ProtoMember(3)]
public Type ValueType { get; set; }
}
serialization code
var e1DataRows = new List<E1DataRow>();
/*
Code to add Data rows to e1DataRows
e1DataRow.AddNameValue(column.ColumnName, value, column.TypeOfColumn);
*/
using (var stream = File.OpenWrite(path))
{
Serializer.Serialize(stream, e1DataRows);
}
[ProtoMember(2, DynamicType = true)]
public object Value { get; set; }
Above code throws following error (DynamicType = true)
ProtoMemberAttribute.DynamicType' is obsolete: 'Reference-tracking and dynamic-type are not currently implemented in this build; they may be reinstated later; this is partly due to doubts over whether the features are adviseable, and partly over confidence in testing all the scenarios (it takes time; that time hasn't get happened); feedback is invited'
It would be great if you can help with how to serialize a List using Protobug-net. Thanks...
You may want to look at https://github.com/dotarj/protobuf-net-data - this isn't affiliated with protobuf-net (different authors etc), but it uses protobuf-net to perform serialization of DataTable and data-readers, so it might do what you want ready-made.
As for implementing it yourself:
protobuf-net does not support object (or dynamic, which is just a fancy way of spelling object), fundamentally. There are ways of working around this, essentially similar to the oneof handling in protobuf - i.e. something like (in protobuf terms):
message Foo {
oneof payload {
string payload_string = 1;
bool payload_bool = 2;
int32 payload_int32 = 3;
float payload_float = 4;
// etc
}
}
This is pretty easy to put together in protobuf-net thanks to "conditional serialization", which means you could do something like:
public object Value { get; set; }
[ProtoMember(1)]
public string ValueString
{
get => (string)Value;
set => Value = value;
}
public bool ShouldSerializeValueString()
=> Value is string;
[ProtoMember(2)]
public string ValueBoolean
{
get => (bool)Value;
set => Value = value;
}
public bool ShouldSerializeValueBoolean()
=> Value is string;
// etc
If on c# >= 4 you might want to try the following:
[ProtoContract]
public class E1DataRow
{
[ProtoMember(1)]
public List<NameValue<dynamic>> NameValues { get; set; }
public E1DataRow()
{
NameValues = new List<NameValue<dynamic>>();
}
public void AddNameValue(string name, dynamic obj)
{
NameValues.Add(new NameValue<dynamic> { Name = name, Value = obj });
}
}
public class NameValue<T>
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public T Value { get; set; }
[ProtoMember(3)]
public Type ValueType { get { return Value.GetType(); } }
}
Not sure if Protobuf-net likes the List<NameValue<dynamic>>, can't test it as of now.
ProtoMember(3) at ValueType possibly is not necessary as of being readonly anyways.
I am deserializing an json object with JsonConvert.DeserializeObject
object is like this:
"journalItemAccounts":"{\"item1\":\"2222\"}"
My deserialize code is:
jsonResult.accountsData.journalItemAccounts != null ? JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonResult.accountsData.journalItemAccounts) : null;
jsonResult.accountsData.journalItemAccounts contains "journalItemAccounts":"{\"item1\":\"2222\"}"
This is deserializing perfectly. But what if i need to deserialize the object like this:
"journalItemAccounts":"{\"item1\":{\"incomeAccount\":\"5030\",\"expenseAccount\":\"\",\"assetAccount\":\"\"}}"
You can start DeserializeObject with Dictionary<string, object> and check whether the value is Object or Value. Here is the example method to convert the Json to pick the value or object
public class item1
{
public string incomeAccount { get; set; }
public string expenseAccount { get; set; }
public string assetAccount { get; set; }
}
public static void ParseJson(string json)
{
var keyValuePairs = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
foreach (var keyvaluepair in keyValuePairs)
{
var obj = keyvaluepair.Value.ToString();
decimal itemvalue;
if (decimal.TryParse(obj, out itemvalue))
{
Console.WriteLine(itemvalue);
}
else
{
var result = JsonConvert.DeserializeObject<item1>(obj);
Console.WriteLine($"{ result.incomeAccount } - {result.expenseAccount} - {result.assetAccount}");
}
}
}
Call this method to get the desired result
ParseJson("{\"item1\":\"2222\"}");
ParseJson("{\"item1\":{\"incomeAccount\":\"5030\",\"expenseAccount\":\"5031\",\"assetAccount\":\"0532\"}}");
Output
2222
5030 - 5031 - 0532
Create a type and pass that like this.
var deserializedObject = JsonConvert.DeserializeObject<Model>(jsonResult.accountsData.journalItemAccounts);
public class Model
{
public AnotherModel item1 { get; set; }
}
public class AnotherModel
{
public string incomeAccount { get; set; }
public string expenseAccount { get; set; }
public string assetAccount { get; set; }
}
You are getting this because you are using string as value. You use an Object type or a class instead.
jsonResult.accountsData.journalItemAccounts != null ? JsonConvert.DeserializeObject<Dictionary<string, Object>>(jsonResult.accountsData.journalItemAccounts) : null;
What would be the best approach for having a class with two distinct fields (with distinct types) but same PropertyName.
It will always be the case that whenever one of the fields has a value, the other one will be null. I know I could create
two different classes, each of them with only one field. Is there a better alternative than creating two different classes?
Here's an example of what I'm trying to accomplish:
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Foo
{
private Foo(int x, bool asAList = false)
{
if (asAList)
{
Baz = new List<int> { x };
}
else
{
Bar = x;
}
}
public static JObject Get(int x, bool asAList = false)
{
Foo foo = new Foo(x, asAList);
return JObject.FromObject(foo);
}
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
public int? Bar { get; set; } = null;
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
public List<int> Baz { get; set; } = null;
}
And I'd like to be able to do this:
JObject a = Foo.Get(1);
JObject b = Foo.Get(2, true);
You could have one private JToken JsonProperty that is being used for serializing/deserializing to either of the two public facing properties. On a set operation, that would then determine based on the JToken type if it's a JArray or not and then based on that determine which of the other properties to set. On a get operation, it would use the property that isn't null and convert that to the JToken. In order to deserialize you'll want a constructor that can be used the [JsonConstructor] can be added. Since you don't want to serialize/deserialize the other properties directly, the [JsonProperty] attribute can be removed because of the MemberSerialization.OptIn.
[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class Foo
{
[JsonConstructor]
private Foo()
{ }
private Foo(int x, bool asAList = false)
{
if (asAList)
Baz = new List<int> { x };
else
Bar = x;
}
public static JObject Get(int x, bool asAList = false)
{
Foo foo = new Foo(x, asAList);
return JObject.FromObject(foo);
}
[JsonProperty(PropertyName = "qwerty", NullValueHandling = NullValueHandling.Ignore)]
private JToken Qwerty
{
get
{
return Bar.HasValue ? JToken.FromObject(Bar) : Baz != null ? JToken.FromObject(Baz) : null;
}
set
{
if (value != null && value.Type == JTokenType.Array)
Baz = value?.ToObject<List<int>>();
else
Bar = value?.ToObject<int?>();
}
}
public int? Bar { get; set; }
public List<int> Baz { get; set; }
}
I'm trying to find out a solution for this, but I'm not sure if it's possible or not.
I have a base class, lets say
public class A
{
[XmlAttribute("Date")]
public DateTime Date {get;set;}
}
and a derived class:
public class B: A
{
[XmlAttribute("Date")]
public new String StringDate {get;set;}
}
I have to serialize a Xml.
The value of "Date" on the Xml, is String and in fact it's not a DateTime format string. But I use "A" for many other stuff so I cannot just change it to String without affecting other parts of the program. Sadly it is not an option.
So my idea is to create a derived class "B" who inherit everything of "A" and overrided the property Date to get it fill from the deserialization and then format it to DateTime.
I read about virtual or abstracts but I'm not acquainted with it and don't have any clue about it, if it is the solution maybe someone can guide me on the first steps.
Anyone can help me?
EDIT
XML:
<Vorgang Vorgang="HQHT8GTQ">
<Vertragsbeginn Vertragsbeginn="20140202" />
</Vorgang>
Class A:
[DataContract(Name = "Vorgang")]
[KnownType(typeof(Vorgang))]
public class Vorgang
{
[IgnoreDataMember]
public DateTime Vertragsbeginn { get; set; }
}
Class B:
public class VorgangOverride : UTILMD.Vorgang
{
private string datestring;
[XmlAttribute("Vertragsbeginn")]
public new String Vertragsbeginn {
get { return datestring; }
set
{
base.Vertragsbeginn = DateUtil.StringToDate(value, EDIEnums.Vertragsbeginn);
datestring = value;
}
}
}
Deserialization method:
private static VorgangOverride Deserialize (XmlNode inVorgang)
{
using (MemoryStream stm = new MemoryStream())
{
using (StreamWriter stw = new StreamWriter(stm))
{
stw.Write(inVorgang.OuterXml);
stw.Flush();
stm.Position = 0;
XmlRootAttribute xRoot = new XmlRootAttribute { ElementName = "Vorgang", IsNullable = true };
var serializer = new XmlSerializer(typeof(VorgangOverride), xRoot);
VorgangOverride podItem = (VorgangOverride) serializer.Deserialize(stm);
return podItem;
}
}
}
EDIT:
Solved using
[XmlRoot("Vorgang")]
public class VorgangOverride
{
public VorgangOverride()
{
}
#region Public Properties
public string datestring;
[XmlElement("Vertragsbeginn")]
public Vertragsbeginn VertragsbeginnAsString { get ; set ;}
#endregion
}
public class Vertragsbeginn
{
[XmlAttribute("Vertragsbeginn")]
public String vertragsbeginn { get; set; }
}
I found the solution:
[DataContract(Name = "Vorgang")]
[KnownType(typeof(Vorgang))]
public class Vorgang
{
[XmlIgnore] // use XmlIgnore instead IgnoreDataMember
public DateTime Vertragsbeginn { get; set; }
}
// this class map all elements from the xml that you show
[XmlRoot("Vorgang")] // to map the Xml Vorgang as a VorgangOverride instance
public class VorgangOverride : Vorgang
{
[XmlAttribute("Vorgang2")] // to map the Vorgang attribute
public string VorgangAttribute { get; set; }
[XmlElement(ElementName = "Vertragsbeginn")] // to map the Vertragsbeginn element
public Vertragsbeginn VertragsbeginnElement
{
get { return _vertragsbeginn; }
set
{
base.Vertragsbeginn = new DateTime(); // here I Assing the correct value to the DateTime property on Vorgan class.
_vertragsbeginn = value;
}
}
private Vertragsbeginn _vertragsbeginn;
}
// this class is used to map the Vertragsbeginn element
public class Vertragsbeginn
{
[XmlAttribute("Vertragsbeginn")] // to map the Vertragsbeginn attriubute on the Vertragsbeginn element
public string VertragsbeginnAttribute { get; set; }
}
later I say:
var string xmlContent =
#"<Vorgang Vorgang2=""HQHT8GTQ"">
<Vertragsbeginn Vertragsbeginn=""20140202"" />
</Vorgang>";
var a = Deserialize<VorgangOverride>(xmlContent);
and this is the method to Deserialize:
// method used to deserialize an xml to object
public static T Deserialize<T>(string xmlContent)
{
T result;
var xmlSerializer = new XmlSerializer(typeof(T));
using (TextReader textReader = new StringReader(xmlContent))
{
result = ((T)xmlSerializer.Deserialize(textReader));
}
return result;
}
You will not be able to override a property with an other class type.
The reason is Polymorphism. (more information: https://msdn.microsoft.com/en-us/library/ms173152.aspx)
You can cast the class B to class A. Which means the class B must have all the properties and methods class A has, too. But in your case class B would have a String rather than a Date called Date. Which is simply not possible.
I'm trying to deserialize a reponse from a REST API.
"<FieldListDTO xmlns=\"api.playcento.com/1.0\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">
<Allfield>
<FieldDTO>
<Fieldname>Mobile nr</Fieldname>
<Fieldtype>T</Fieldtype>
<Fieldvalue>003241234578</Fieldvalue>
<Fk_id_page>CP584ea74ce5ad4e2d8561d75fc6944f96</Fk_id_page>
<Id_field>FI152dcde5ef9849898b12d6a3f2cdb4ee</Id_field>
<Required>true</Required>
</FieldDTO>
</Allfield>
<Totalcount>1</Totalcount>
</FieldListDTO>"
The Field class:
namespace PlaycentoAPI.Model
{
[XmlRoot("FieldListDTO",Namespace = "api.playcento.com/1.0")]
[XmlType("FieldListDTO")]
public class FieldListDTO
{
public FieldListDTO() { }
[XmlElement("Totalcount")]
public int TotalCount { get; set; }
[XmlArray("Allfield")]
[XmlArrayItem("FieldDTO", typeof(Field))]
public Field[] Field { get; set; }
}
[XmlRoot("FieldDTO", Namespace = "api.paycento.com/1.0")]
[XmlType("FieldDTO")]
public class Field
{
public Field()
{
}
[XmlElement("Id_field")]
public string ID_Field { get; set; }
[XmlElement("Fieldtype")]
public string FieldType { get; set; }
[XmlElement("Fk_id_page")]
public string FK_ID_PAGE { get; set; }
[XmlElement("Required")]
public bool Required { get; set; }
[XmlElement("Fieldname")]
public string FieldName { get; set; }
[XmlElement("Fieldvalue")]
public string FieldValue { get; set; }
}
}
My code which calls the API and deserializes it:
string response = Helper.PerformAndReadHttpRequest(uri, "GET", "");
FieldListDTO myObject;
XmlReaderSettings settings = new XmlReaderSettings();
using (StringReader textReader = new StringReader(response))
{
using (XmlReader xmlReader = XmlReader.Create(textReader, settings))
{
XmlSerializer mySerializer = new XmlSerializer(typeof(FieldListDTO));
myObject = (FieldListDTO)mySerializer.Deserialize(xmlReader);
}
}
return myObject.Field;
In my actual response, I'm getting back 14 FieldDTO's. After deserializing the xml, FieldListDTO myObject contains TotalCount = 14 and Field is an array containing 14 Field's. But all the properties of these fields are NULL (or false).
I'm using the same method for several other API calls. I've compared the classes and the only difference that I see is that the class (Field) has an bool property. So I thought that was the problem. I've changed the bool property to a string but still all the properties were NULL after deserialization.
First thing to catch my eye is that the namespace in your FieldDTO class doesn't match the one in the XML document.
"api.paycento.com/1.0"
"api.playcento.com/1.0"