When I seralise an object, for some string properties, I would like to output empty string other than ignore or output null.
According to newton's doc, I could do this:
public class Data
{
public int ProductId { get; set; }
[DefaultValue("")]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)]
public string LargeData { get; set; }
}
However, in my test case, it still output null
Data D1 = new Data()
{
ProductId = 1
};
var b = JsonConvert.SerializeObject(D1);
The output is {"ProductId":1,"LargeData":null}. Am I doing something wrong?
Looking at DefaultValueHandling it doesn't look like there's any way of doing what you want.
The default value attribute is only used when deserializing, if the property isn't specified in the JSON. The ignore / include choices are the ones which are relevant when serializing, and those don't affect the value that's serialized - just whether or not it should be serialized.
Unless you've got code which actually sets the value to null, the simplest option would be to make the property default to "" from a .NET perspective:
public class Data
{
public int ProductId { get; set; }
public string LargeData { get; set; } = "";
}
Related
I need to get an xml output like this:
<Partners>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</Partner>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</Partner>
</Partners>
It has to be possible to add multiple PartnerID tags and multiple PartnerIDType tags in the parner tag. To get this result I was using a list of PartnerIdInfo. I had my classes like this:
public class Someclass
{
public List<Partner> Partners {get; set;}
}
public class Partner
{
public string PartnerType { get; set; }
[XmlElement("PartnerIdInfo")]
public List<PartnerIdInfo> PartnerIDInfos { get; set; }
}
public class PartnerIdInfo
{
public string PartnerID { get; set; }
public string PartnerIDType { get; set; }
}
The [XmlElement("PartnerIdInfo")] tag is for not showing the tag from the list property in the partner class. But it is still showing the tag :
<Partners>
<Partner>
<PartnerType>SomeStringValue</PartnerType>
<PartnerIdInfo>
<PartnerID>SomeStringValue</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</PartnerIdInfo>
<PartnerIdInfo>
<PartnerID>BTW BEXXXXXXXXXX</PartnerID>
<PartnerIDType>SomeStringValue</PartnerIDType>
</PartnerIdInfo>
</Partner>
</Partners>
Is there an attibute (or another solution) to do this? I prefer not to rewrite my code and fill everything in the code through the use of XmlDocument and XmlNode... Or will this be the only possible solution for this outcome?
Your problem is that you want the serializer to combine the value of Partner.PartnerType with the repeating values of Partner.PartnerIDInfos[*].PartnerID and Partner.PartnerIDInfos[*].PartnerIDType into a single omnibus <Partner> element. Unfortunately, this simply is not implemented. The content of each PartnerIdInfo will always be serialized within its own element.
Several workarounds can be found in the answers to the questions Keep sort when deserialize and serialize XML using XmlSerializer and Xml Deserialization - Merging two elements into a single List<T> object. In particular using a polymorphic array of simple elements with two possible types corresponding to <PartnerID> and <PartnerIDType> should meet your needs. You will need to modify Partner to mark the PartnerIDInfos as ignored and use a surrogate array for the elements:
public class Partner
{
[XmlElement(Order = 1)]
public string PartnerType { get; set; }
[XmlIgnore]
public List<PartnerIdInfo> PartnerIDInfos { get; set; }
[XmlElement(typeof(PartnerID), Order = 2), XmlElement(typeof(PartnerIDType), Order = 2)]
public StringElementBase [] XmlPartnerIDInfos
{
get => PartnerIDInfos?.SelectMany(p => new StringElementBase [] { new PartnerID { Value = p.PartnerID }, new PartnerIDType { Value = p.PartnerIDType } }).ToArray();
set => PartnerIDInfos = value?.OfType<PartnerID>().Zip(value.OfType<PartnerIDType>(), (id, type) => new PartnerIdInfo { PartnerID = id.Value, PartnerIDType = type.Value }).ToList();
}
}
public abstract class StringElementBase { [XmlText] public string Value { get; set; } } // From https://stackoverflow.com/a/48130816/3744182
public class PartnerID : StringElementBase { }
public class PartnerIDType : StringElementBase { }
Notes:
Marking StringElementBase.Value with [XmlText] causes the value to be serialized as text rather than nested markup.
StringElementBase [] XmlPartnerIDInfos should be an array rather than a List<StringElementBase> because XmlSerializer will not set back the value of a resizable collection such as List<T> after populating it.
Setting XmlElement.Order is optional but does indicate that <PartnerType> not come between any of the <PartnerID> and <PartnerIDType> elements.
In the setter for XmlPartnerIDInfos I don't attempt to validate that there are an equal number of PartnerID and PartnerIDType elements, or that the values alternate. You could add that, if you wish.
Demo fiddle here.
My given class is :
public class Myclass
{
public int id { get; set; }
public string name{ get; set; }
}
I am passing jsonString like this:
var jsonString = #"{ 'name': 'John'}".Replace("'", "\"");
When i try to deserialize above json string using following code :
var visitData = JsonConvert.DeserializeObject<Myclass>(jsonString, jsonSerialize);
I am getting following values in visitData :
id : 0
name : "john"
But i want to ignore the id property as it is not present in jsonString.
How should i implement this functionality in Console Application of .Net Core 3.1 in C#.
You can try to declare id as a nullable property
public class Myclass
{
public int? id { get; set; } // a nullable property
public string name{ get; set; }
}
Usually deserializer will look for the matching property from the json string, if not exist then the default value is assigned. In your case the default value of int is 0. Again if you make the int as nullable int then again the default value will be assigned i.e., null.
For Newtonsoft.Josn create a contract resolver and manage your serialization/deserialization. Please find the detail information here Should not deserialize a property
I have to process a complex JSON-file and I'm hitting some obstacles.
Below you will find a little excerpt from the class which is in XML but which I convert to JSON to be able to process it easier.
<Selection Category="M43002NN">
<ReferendumOptionIdentifier>foo</ReferendumOptionIdentifier>
<ValidVotes>6162</ValidVotes>
<CountMetric Id="M" Type="LevelDepositList">43002</CountMetric>
<CountMetric Id="S4" Type="SeatsToBeFilled">23</CountMetric>
<CountMetric Id="S5" Type="SubstitutesMax">0</CountMetric>
<CountMetric Id="S9" Type="LinguisticRegime">2</CountMetric>
<CountMetric Id="S10" Type="VotesDeposited">6620</CountMetric>
<CountMetric Id="S11" Type="BlankAndInvalidVotes">458</CountMetric>
<CountMetric Id="S12" Type="Alderman">0</CountMetric>
<CountMetric Id="S14" Type="ValidVote_E5">0</CountMetric>
<CountMetric Id="S15" Type="BlankAndInvalidVotes_E5">0</CountMetric>
</Selection>
In the above example I'm trying to extract the value of the CountMetric which has the type "SeatsToBeFilled".
So far I was able to collect the results and to isolate the correct CountMetric but I can't seem to get it's value.
This is my class:
public class TotalSelectionClass
{
[JsonProperty("#Category")]
public string Category { get; set; }
public int ValidVotes { get; set; }
public List<CountMetricClass> CountMetric { get; set; }
}
And this is the CountMetricClass that I use:
public class CountMetricClass
{
[JsonProperty("#Id")]
public string Id { get; set; }
[JsonProperty("#Type")]
public string Type { get; set; }
}
Below is the code that I use to get the desired CountMetric (slightly reduced code for readability-purposes):
var TotalSeats = Selection[0].CountMetric.Where(x => x.Type == "SeatsToBeFilled").First();
This returns the CountMetric-object to me but how do I extract the value from it? So in this case, how do I extract the number 23 from it?
Thank you.
The answer here really depends on the shape of your JSON and, thus, how you're doing your XML -> JSON conversion. You have not provided the JSON form nor asked for help in converting the XML -> JSON so I'll answer based on the information available.
As-is, you can't get the CountMetric value from your CountMetricClass. This is because the value held in the XML (looks like 23 for SeatsToBeFilled) is never read into that object. To get the value, you'll need to
1) check that your XML converter is actually encoding the value from the XML into the JSON to be parsed
2) modify your CountMetricClass so that it's reading from this value field. I've chosen to call this field MetricValue for readability, but you could obviously choose whatever name works best for you. For instance, your new class may take the form of something like:
public class CountMetricClass
{
[JsonProperty("#Id")]
public string Id { get; set; }
[JsonProperty("#Type")]
public string Type { get; set; }
[JsonProperty("#MetricValue")]
public int MetricValue { get; set; }
}
3) once we've successfully read the MetricValue into your CountMetricClass, we can modify your linq expression to get the value by accessing the MetricValue field from the target CountMetric like:
var TotalSeats = Selection[0].CountMetric.Where(x => x.Type == "SeatsToBeFilled").MetricValue;
A little follow-up for those who might run into the same issue.
Based on SIRHAMY's suggestion I had a look at the actual JSON which I was converting from XML.
{"#Id":"S4","#Type":"SeatsToBeFilled","#text":"23"}
As SIRHAMY suggested I created a Text-field in my CountMetricClass
public class CountMetricClass
{
[JsonProperty("#Id")]
public string Id { get; set; }
[JsonProperty("#Type")]
public string Type { get; set; }
[JsonProperty("#Text")]
public string Text { get; set; }
}
After that I could get the value of the text by using:
Selection[0].CountMetric.Where(x => x.Type == "SeatsToBeFilled").First().Text
This will return "23" to me.
Thank you SIRHAMY!
I have an XML coming in this form:
<run>
<foo status="1">111.9</foo>
<fred status="0">5.5</fred>
</run>
I would like to deserialize this in either of these forms below (I'm undecided, and hoping an answer will help me decide, although I tend to prefer #1, for design aesthetics as much as anything else):
Case # 1
[Serializable]
public class DataValue
{
[XmlAttribute("status")]
public int Status { get; set; }
// I need something here, but what?
public float Value { get; set; }
}
[Serializable]
[XmlRoot("run")]
public class DataBag
{
[XmlElement("foo")]
public DataValue Foo{ get; set; }
[XmlElement("fred")]
public DataValue Fred{ get; set; }
}
When I try this, I get a value of 0 for either member foo or fred.
Case # 2
[Serializable]
[XmlRoot("run")]
public class DataBag2
{
[XmlElement("foo")]
public float Foo{ get; set; }
[XmlElement("foo")]
[XmlAttribute("status")]
public int Foo_status { get; set; }
[XmlElement("fred")]
public float Fred{ get; set; }
[XmlElement("fred")]
[XmlAttribute("status")]
public int Fred_status { get; set; }
}
It compiles but I get an InvalidOperationException while reflecting Foo_status, for which the innermost exception is "For non-array types, you may use the following attributes: XmlAttribute, XmlText, XmlElement, or XmlAnyElement."
What can I do to end up with an actual value in case #1, or no exception (and a valid value and status) for case #2?
The code for the serialization goes like this:
// Case 1
using (var sr = new StreamReader("data.xml"))
{
var xs = new XmlSerializer(typeof(DataBag));
var run = (DataBag)xs.Deserialize(sr);
Console.WriteLine("Got a run: {0}-{1}", run.Fred.Value, run.Fred.Status);
// Issue here is that value is always 0, but status is accurate
}
// case 2
using (var sr = new StreamReader("data.xml"))
{
var xs = new XmlSerializer(typeof(DataBag2));// Exception here
var run = (DataBag2)xs.Deserialize(sr);
Console.WriteLine("Got a run: {0}-{1}", run.Foo, run.Foo_status);
}
Thanks for your attention!
For case 1 you just need to mark it as XMLText:
[XmlText]
public float Value { get; set; }
You want to use [XmlText]:
Indicates to the XmlSerializer that the member must be treated as XML text when the class that contains it is serialized or deserialized.
Thus:
public class DataValue
{
[XmlAttribute("status")]
public int Status { get; set; }
[XmlText]
public float Value { get; set; }
}
Case #2 just won't work as you want. Adding [XmlAttribute("status")] to Foo_status means that Foo_status will be serialized as an attribute of DataBag2, not Foo. Applying [XmlElement("foo")] as well then says it's an element of DataBag2, which is of course in conflict with other attribute.
There's no way with XmlSerializer for an outer container type to specify an attribute to be applied to a nested element.
I have a REST-Service build with ServicStack and in one call the user can send different types of values. So I made the property in C# of type object.
The JSON that is sent looks like this:
{"name":"ffff","params":[{"pId":1,"value":[624,625]},{"pId":2,"value":"xxx"}]}
The part "value":[624,625] results in a string object filled with "[624,625]". I was hoping to get an int-array or at least a string array, but it is plain string.
I set JsConfig.TryToParsePrimitiveTypeValues = true, but that doesn't seem to have any effect.
I tried the latest sources from github.
Can this be done with any combination of switches or must I parse this myself?
Thanks
EDIT:
Here is some testcode:
[TestMethod]
public void JsonTest()
{
string json = "{\"name\":\"ffff\",\"params\":[{\"pId\":1,\"value\":[624,625]},{\"pId\":2,\"value\":\"xxx\"}]}";
var x = JsonSerializer.DeserializeFromString<xy>(json);
Assert.AreEqual(x.Params[0].Value.GetType(), typeof(int[]));
}
public class xy
{
public string Name { get; set; }
public List<Param> Params { get; set; }
}
public class Param
{
public int PId { get; set; }
public object Value { get; set; }
}
If you change the type of "Value" to int array as follows, then ServiceStack will serialize to array of int.
public class Param
{
public int PId { get; set; }
public int[] Value { get; set; }
}
The following unit test passes:
[TestMethod]
public void JsonTest()
{
string json = "{\"name\":\"ffff\",\"params\":[{\"pId\":1,\"value\":[624,625]},{\"pId\":2,\"value\":\"xxx\"}]}";
var x = JsonSerializer.DeserializeFromString<xy>(json);
Assert.AreEqual(x.Params[0].Value.GetType(), typeof(int[]));
// Show that we have some integers
Assert.IsTrue(x.Params[0].Value.Count()>0);
}
If you cannot change the type of Value for any reason, then you can use ServiceStack.Text to serialize the string into an array as needed.