Remapping a single tag while deserializing - c#

I've refactored code like this:
public string CamelCASE { get; set; }
to:
public string CamelCase {get; set; }
only do discover that the input XML contains the former casing (let's call it a shouting camel). I have no control over how the XML document is produced. Nor do I burn of desire to retract my changes.
I'd like to map the loud camel property to a softly speaking one.
I've tried XmlElement and XmlMapping but to no greater success. A googling gave me only hits on how to map stuff to attributes, along lines of this post. However, I need only something like <LoudCAMEL> to be deserialized to a property public string QuietCamel.
Is there a smooth way to do so?
Edit
After adding the attribute as follows:
using System.Collections.Generic;
using System.Xml;
public class Beep : SuperBeep
{
private readonly BeepType _a;
public Beep() { _a = BeepType.SomeSome; }
public Beep(BeepType input) { _a = input; }
~Beep() { }
public override void Dispose() { }
public BeepType Aaa { get { return _a; } }
[XmlElement("CamelCASE")]
public bool CamelCase { get; set; }
}
I can see the red, wavy highlight telling me Cannot access constructor 'XmlElement' here due its protection level. When I compile, though, I get the IDE crying out loud that 'System.Xml.XmlElement' is not an attribute class.
Frankly, I'm a bit confused by the suggestion to use attributes (this is targeting .NET 2.0), since I was under the impression that attributing wasn't available to .NET prior to version 3.5. Am I mistaken?

[XmlElement("CamelCASE")]
public string CamelCase { get; set; }
should be all you need, if you are keeping the shouty name in the xml. If you want to use the quieter name in new xml, but allow the old name to still work, it gets more complicated. You could use:
public string CamelCase { get; set; }
[XmlElement("CamelCASE"), Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public string CamelCaseLegacy {
get { return CamelCase; }
set { CamelCase = value; }
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeCamelCaseLegacy() { return false; }
When serializing, the CamelCase property will serialize to <CamelCase>, and the CamelCaseLegacy element will be ignored due to the ShouldSerialize* method. However, when deserializing, the CamelCaseLegacy property will be used whenever <CamelCASE> is seen. We then map this value back to the CamelCase property.

You are referring to the wrong namespace.
Remove
using System.Xml;
and add
using System.Xml.Serialization;

Related

Why does the JSON2CSharp online converter decorate each property with a [JsonProperty("...")] attribute?

I have this JSON response from an API:
{
"arguments": {
"Configuration": {
"Building_Configuration.Parameters_SP.fixtureStrategy_SP": "ETA",
"Building_Configuration.Parameters_SP.dimensionSelection_SP": "Imperial",
"Building_Configuration.Parameters_SP.controllerRobotic_SP": false,
"Building_Configuration.Parameters_SP.controllerBACNet_SP": false
}
}
}
I have this Root.cs Model file that I used the JSON to C# Converter to make that corresponds to the JSON above in my solution in C# Visual Studio 2019:
public class Root
{
public Arguments arguments { get; set; }
}
public class Arguments
{
public Configuration Configuration { get; set; }
}
public class Configuration
{
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
public string BuildingConfigurationParametersSPFixtureStrategySP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.dimensionSelection_SP")]
public string BuildingConfigurationParametersSPDimensionSelectionSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerRobotic_SP")]
public bool BuildingConfigurationParametersSPControllerRoboticSP { get; set; }
[JsonProperty("Building_Configuration.Parameters_SP.controllerBACNet_SP")]
public bool BuildingConfigurationParametersSPControllerBACNetSP { get; set; }
}
I'm trying to access and return the value of BuildingConfigurationParametersSPFixtureStrategySP (the first property in the Configuration class above) in this manner:
public string CreateExecPost()
{
/*...everthing here works fine down through the end of this method. I can set
breakpoints and step through the following code and look at the values of all the
following variables and all is well
....*/
var payload = new StringContent(newPost, Encoding.UTF8, "application/json");
var result = client.PostAsync(endpoint, payload).Result.Content.ReadAsStringAsync().Result;
Root MyObject = JsonConvert.DeserializeObject<Root>(result);
return MyObject.arguments.configuration.BuildingConfigurationParametersSPFixtureStrategySP;
} //The correct value for BuildingConfigurationParametersSPFixtureStrategySP is returned and all is well!
So, the question is why does the converter generate an attribute like...
[JsonProperty("Building_Configuration.Parameters_SP.fixtureStrategy_SP")]
...above each of the { get; set; } statements? I've researched and read about JsonProperty and JsonPropertyAttribute, but I'm still not fully clear on it. I'm not seeing what the tool uses to signal it to generate the attribute or why it does it.
This tool generates code with the Json.net library by default, and the official documentation doesn’t explain much on this class: https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_JsonProperty.htm
There are other similar questions on how to use this, for example:
What [JsonProperty] used for in c#?
The general usage of this class is when you want to, or need to, rename a property. And the last one is relevant here.
The json parser normally tries to match the property names 1:1 with your class properties.
But whenever the json property name contains reserved keywords, language syntax, or otherwise illegal property names, you need to rename it.
In your example the name Building_Configuration.Parameters_SP.fixtureStrategy_SP contains periods. If you would try to name your get;set property like this, the code would not compile.
The site that generates the code knows this, and will add the required JsonProperty to map the full json property name to the class field that was renamed to BuildingConfigurationParametersSPFixtureStrategySP (the illegal characters were removed)
See for valid property names: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/identifier-names
And for reference: Accessing properties with a dot in their name

Making property [NonSerialized] for a value

I am trying to figure out how can i make a property [NonSerialized] for a value
Check this out :
using System;
using System.Text.Json;
class Test
{
public static bool GoingToBeSerialized = false;
public int PaymentForTheDay { get; set; }
public int NumberOfDays { get; set; }
// i want to disable it if GoingToBeSerialized is true
[System.Text.Json.Serialization.JsonIgnore]
public int TotalPayment;
public bool ShouldSerializeTotalPayment() => GoingToBeSerialized;
}
Thanks.
Note that [Serializable] and [NonSerialized] (in the original question, now removed in the edit) do nothing with most serializers - they only apply to BinaryFormatter, which you aren't using.
There's a very good chance that simply using:
public int TotalPayment {get;set;}
public bool ShouldSerializeTotalPayment() => GoingToBeSerialized;
will do what you want; with the recent addition of your pastebin that shows you're using Json.NET, this should indeed work - conditional serialization is a Json.NET feature using the standard pattern. Note also that I made TotalPayment a property, and removed the [JsonIgnore].

How to bind a custom attribute value to a property?

I want to create a custom attribute that when decorated on a property, it "sets" the property to the value of the attribute. In this case, I am reading an excel file and want to map its value to a property.
using System;
namespace Excel.DataBind
{
[AttributeUsage(AttributeTargets.Property)]
public class ExcelDataBindAttribute : Attribute
{
private ExcelReader _excelReader;
public ExcelDataBindAttribute(string rangeAddress)
{
_excelReader = new ExcelReader();
_excelReader.GetExcelValue(rangeAddress);
// some code here to map it to the property it decorates...?
}
}
}
namespace Excel.Models
{
public class Model
{
[ExcelDataBind("A2")]
public string Value { get; set; }
}
}
I'm searching online to find a way to achieve this, but reflection is said as a good approach. But as i'm new to this, i'm not sure if it would be the best approach. Can someone here direct me?
Thank you.
First of all, the attribute should (as the name suggests) only decorate a model a such. An separate binder class should than do the magic. Something like this:
using Excel.DataBind;
using System;
using System.Collections.Generic;
using System.Reflection;
namespace Excel.DataBind
{
public class ExcelDataBinder
{
public void DataBind(ExcelDocument doc, object target)
{
var lookup = new Dictionary<string, PropertyInfo>();
// loop through all properties of the target.
foreach(var prop in target.GetType().GetProperties())
{
// if the property has an decorator, store this.
var address = prop.GetCustomAttribute<ExcelDataBindAttribute>()?.Address;
if(!string.IsNullOrEmpty(address))
{
lookup[address] = prop;
}
}
// loop through all excel fields
foreach(var field in doc)
{
// if a mapping is defined
if(lookup.TryGetValue(field.Address, out var prop))
{
// use reflection to set the value.
prop.SetValue(target, field.Value);
}
}
}
}
[AttributeUsage(AttributeTargets.Property)]
public class ExcelDataBindAttribute : Attribute
{
public ExcelDataBindAttribute(string address) => Address = address;
public string Address { get; }
}
}
namespace Excel.Models
{
public class Model
{
[ExcelDataBind("A2")]
public string Value { get; set; }
}
}
This approach can also be used to to write to Excel based on a model of course.
Note that setting the value can be tricky. Your ExcelDocument representation might use different types than your model (decimal vs double etc.) In that case you have to convert that too.
Another remark: In my experience (I've written code like that in the past) in real world scenario's the model represent just a row of an excel sheet tab. Than you need something with an header row, and should be defensive on column orders. (You still need attributes to describe the relation between the Excel truth and you code truth however).

Map enum to json property

I have a class called InstrumentConfigValues with properties that has type implementing an interface. Now I have an enum by name InstrumentConfig which has set of values. These values are like keys inside the json file. I want to map something like [JsonProperty(InstrumentConfig.LowDiskpace.ToString()].
For some reason its not allowing this and complains saying:
An attribute argument must be constant expression
I referred to many post specifically JsonStringEnumConverter. But how can I map each property with the enum key. I also saw this post JsonSerializationSettings but not able to correlate to my problem. Please help/
public class InstrumentConfigValues : IInstrumentConfig
{
public double SpaceNeededForSingleRun
{
get; set;
}
public int NumberOfInputSlots
{
get; set;
}
public int SupportedChannelCount
{
get; set;
}
}
//I want this inheritance as some other class wants to access the values.
public abstract class InstrumentConfigReadWrite : InstrumentConfigValues
{
protected ReturnCodes PopulateValuesFromJObject(JObject jObject, string path)
{
try
{
if (JsonConvert.DeserializeObject<InstrumentConfigValues>(jObject.ToString()) == null)
{
return ReturnCodes.ErrorReadingFile;
}
}
catch (JsonSerializationException jex)
{
SystemDebugLogLogger.LogException(jex, "Invalid Instrument Config File Values. Data needs to be copied over.");
return ReturnCodes.ErrorReadingFile;
}
return ReturnCodes.Success;
}
}
As long as you're using a current compiler, you can use nameof.
[JsonProperty(nameof(InstrumentConfig.LowDiskpace))]
If you try using this, and get an error like Compilation error: The name 'nameof' does not exist in the current context, that means you're not using a current compiler. The nameof keyword was introduced in C# 6.0/Visual Studio 2015--anything newer than that should be fine.

Prevent Property from being serialized

I tried something like this:
[NonSerialized]
private string _DecodeText;
public string DecodeText { get { return _DecodeText; } set { _DecodeText = value; } }
But it does not work. "DecodeText" is still in the serialized file. How can i prevent the property from serializing?
I Suspect you're using the XmlSerializer? If so use the [XmlIgnore] attribute instead.
This should be applied to the property instead of the backing field as the XmlSerializer serializes public fields and properties (whereas the BinaryFormatter uses refelction to get at the private fields - hence the marking of the private field with NonSerialized when using a BinaryFormatter).
I was able to use the following and not have the property serialized (.NET 4.0):
private string _DecodeText;
[System.Xml.Serialization.XmlIgnore]
public string DecodeText { get { return _DecodeText; } set { _DecodeText = value; } }
Updated Answer
The [NonSerialized] atttibute is on the variable not the property, but it cannot be on the attribute. So it is not going to help.
One way to prevent the property being serialized is to add a method
public bool ShouldSerializeDecodeText() {
return false;
}
and this (for the XmlSerializer at least) will prevent the property being serialized.
If you don't want to add lots of methods to the class just for serialization you might try inheriting from it and adding the methods to the derived class.
hth,
Alan.
I built on top of #John's answer and modified ef.tt template
to include [System.Xml.Serialization.XmlIgnore]
Here is the code
foreach (var navigationProperty in navigationProperties)
{
if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
{
#>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
[System.Xml.Serialization.XmlIgnore]
<#
}
#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
}
I think this code that will be help you all. With properties you declared and you want it to be serialized only. Then you should add a method return type as boolean and name method is ShouldSerialize as prefix with [NameProperty]. A scratch code as below and link reference to Newtonsoft for you:
public class DisplayFieldSetting
{
public bool ShouldSerializeHidden()
{
return false;
}
public bool ShouldSerializeKeepOriginialColumnName()
{
return false;
}
public string Hidden { get; set; }
public string KeepOriginialColumnName{ get; set; }
}

Categories