How to change xml attribute while deserialization with xmlserializer c#? - c#

Is there any way of modifying attribute value when deserializing xml using XmlSerializer?
For instance, I have such xml:
<chunkList>
<chunk id="ch1" type="p">
<sentence id="s1">
<tok>
<orth>XXX</orth>
<lex disamb="1">
<base>XXX</base>
<ctag>subst:sg:nom:f</ctag>
</lex>
</tok>
</sentence>
</chunk>
</chunkList>
I want to deserialize chunk element into Chunk class and set attribute id="ch1" to Id property - is there any way of trimming this ch substring and asigning number 1 to property of type int?
[XmlAttribute("id")] //maybe there is some attribute to achive this?
public int Id { get; set; }
I have read some of MSDN documentation but didn't found any solution.

There is no elegant way to achieve this using a single attribute. The only way I know to achieve the desired result is to make use of [XmlIgnore] and to create a second property specifically for the stringified xml ID, and a localized converter property for your internal integer value. Some along the lines of:
[XmlAttribute("id")]
public string _id_xml {get; set;}
[XmlIgnore]
public int Id {
// convert local copy of xml attribute value to/from int.
get => int.Parse(_id_xml.Replace("ch",""));
set => _id_xml = $"ch{value}";
}
My converter here is very basic and clearly you will need to improve it and consider error handling.
The serializer will operate against the [XmlAttribute] as normal, but pass over the [XmlIgnore]. Your c# code could use either.
Unfortunately, the XmlSerializer requires public properties, so you can not hide the _id_xml property from your code, but you could use [Obsolete] to signal a warning in the compiler.
You could do the conversion to/from int with the _id_xml getter & setter, but doing this could be problematic when managing errors during serialization.

Related

C# XmlSerializer is apparently ignoring XmlTextAttribute and escaping XML fragment in a string property of the object being serialized

I'm troubleshooting some old existing .Net 4.6.1 code that is XML serializing this class:
public class Orders
{
private int _pagenumber = 0;
[XmlAttribute]
public int pages
{
get { return _pagenumber; }
set { _pagenumber = value; }
}
[XmlText]
public string OrdersXml { get; set; }
}
The OrdersXml string contains a block of already-XML-serialized Order objects (i.e. XML text like: "<Order><OrderId>1</OrderId>...</Order><Order>...</Order>..."). (They are being XML serialized elsewhere for a variety of reasons and this is not subject to redesign.)
The intent is to include that block of XML verbatim in the serialization of this Orders object - in other words, as if string OrdersXml was instead an Orders[] OrdersXML being serialized as part of the Orders object, ending up like: <Orders pages="6"><Order><OrderID>123456</OrderID>...</Order>...</Orders>
But that's not happening. The XML in the OrdersXml property is being serialized as XML-escaped plain text, and it's coming out "<Orders pages="6"><Order><OrderID>2</OrderID>..." - the code is doing post-serialization cleanup to reverse that, and it's coming out useably correct in most cases. I'd rather it serialize correctly in the first place...
I've tried using [XmlText(typeof(string))] instead but that didn't help.
Is the XmlSerializer ignoring the [XmlText] attribute on OrdersXml, or is that not what [XmlText] is intended to do?
What is the "correct" best-practice way to composite XML like this?

Serializing Guid to MongoDB as a BsonString and not BinData?

I have a class that has the following property
[BsonId]
public Guid EventId { get; private set; }
I would like to serialize(and deserialize) the class that has this property to MongoDB with the use of the ToBsonDocument method. Though through the use of the default serializer the resulting BSON type for the _id field is
"_id" : BinDate(3, "wX9ZnP0ApEWF0d5aXLgiUA==")
I would like it to be stored as a BsonString. I plan to create a custom SerializerBase<> extension class in the future to properly deserialize the string back to a Guid I just have not gotten there yet.
I know that I could simply change my property to be a String
[BsonId]
public String EventId { get; private set; }
But I would like to keep it as a Guid. I think to solve my problem I need to make use of a serialization tag but I am not sure which one, any ideas?
Actually I somehow managed to find my answer right after I posted this question.
By making use of the tag BsonRepresentation I can specify the BSON data type that I would like MongoDB to use when serializing the data.
[BsonRepresentation(BsonType.String)]

Adding suffix to XmlElement C# parser

I'm dealing with a XML file which has support for different languages, I want to parse this XML into C# classes using XDocument/XElement (using System.Xml.Serialization). The XML is slightly complex but what I want to achieve should be simple, yet I can't figure it out.
Basix XML example:
<root>
<word_EN>Hello</word_EN>
<word_DE>Hallo</word_DE>
<word_FR>Bonjour</word_FR>
<root>
How I want my parser to look like:
[XmlRoot("root")]
public class Root
{
[XmlElement("word_" + LanguageSetting.SUFFIX)]
public string word { get; set; }
}
I want to get the suffix from another class and I want to be able to change it. I can set the suffix as a const string but then I can't change it. Using a global variable also does not work.
static class LanguageSetting
{
private static string _suffix = "EN";
public static string SUFFIX
{
get { return _suffix; }
set { _suffix = value; }
}
}
Error:
An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type
What is the proper way of adding the suffix?
The correct way of doing this would be for your language suffix to be an XML attribute on the word element but this may not be possible for you.
You are receiving this error because a compile time constant must be use in attribute decorations. LanguageSetting.Suffix is static, but not a constant. Try using the const keyword instead.
In XML, different tag names represent different object types. The best solution for your current XML document is you have seperate classes for each supported language, all inherited from a common class (eg. WordBase).

Deserialize XML element presence to bool in C#

I'm trying to deserialize some XML from a web service into C# POCOs. I've got this working for most of the properties I need, however, I need to set a bool property based on whether an element is present or not, but can't seem to see how to do this?
An example XML snippet:
<someThing test="true">
<someThingElse>1</someThingElse>
<target/>
</someThing>
An example C# class:
[Serializable, XmlRoot("someThing")]
public class Something
{
[XmlAttribute("test")]
public bool Test { get; set; }
[XmlElement("someThingElse")]
public int Else { get; set; }
/// <summary>
/// <c>true</c> if target element is present,
/// otherwise, <c>false</c>.
/// </summary>
[XmlElement("target")]
public bool Target { get; set; }
}
This is a very simplified example of the actual XML and object hierarchy I'm processing, but demonstrates what I'm trying to achieve.
All the other questions I've read related to deserializing null/empty elements seem to involve using Nullable<T>, which doesn't do what I need.
Does anyone have any ideas?
One way to do it would be to use a different property to get the value of the element, then use the Target property to get whether that element exists. Like so.
[XmlElement("target", IsNullable = true)]
public string TempProperty { get; set; }
[XmlIgnore]
public bool Target
{
get
{
return this.TempProperty != null;
}
}
As even if an empty element exists, the TempProperty will not be null, so Target will return true if <target /> exists
Can you explain why you dont want to use nullable types?
When u define an int (as opposed to int?) property in ur poco, it doesnt really represent the underlying xml, and u will simply get the default values for those variables.
IF u assume you wont get empty/null strings or integers with the value 0 in ur xml, youcan used the method Balthy suggested for each of ur properties, or use the method described here
Generally i think its a better idea to create a schema to describe ur xml, and generate classes based on it, while using nullable types, if you really want your classes represent the underlying data.

Is it possible to serialize property attributes to XML attributes in C#?

Background: This question is about logging the change tracking of a POCO class in C# .NET 4.0.
Let's say I have a Person class with a Name (string) property. That Name property has a custom Attribute called [IsDirty(true/false)] that is set dynamically by a property-auditing class.
[IsDirty(true)]
public string Name { get; set; }
After the changes are detected and the attributes are set, I'm storing the object via normal XML Serialization in a MS SQL Database (XML column type).
What I can't figure out is if it's possible to somehow serialize my custom attribute IsDirty along with it's current value - preferably as an XML attribute on the serialized XML element (Name) so that the final xml is like:
<Name IsDirty="true">John</Name>
Any ideas/info would be appreciated-
I think you're going to have to write your own XML serialization for this and mix in some reflection to check attribute values on properties.
There's a good guide to implementing the IXMLSerializable interface here. Unfortunately you will have to implement serialization of all properties in the class, but on the bright side, if you implement IXmlSerializable correctly, you can still use the XmlSerializer class.
In your serialization code, you can check the attribute value using something like this:
public class YourClass : IXmlSerializable
{
[IsDirty(true)]
public string Name { get; set; }
// skipped ReadXml and GetSchema interface methods for brevity
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("YourClass");
var myType = typeof(YourClass);
foreach(var propInfo in myType.GetProperties())
{
writer.WriteStartElement(propInfo.Name);
foreach(var attr in propInfo.GetCustomAttributes(typeof(IsDirtyAttribute), false))
{
var myAttr = attr as IsDirtyAttribute;
writer.WriteAttributeString("Dirty", attr.Value ? "true" : "false");
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
This code is untested and written from memory, so there are probably some bugs lurking around, but hopefully it'll get you on the right track.
It would be possible by manually reading your Attribute and its value. You could do this by wrapping the serialization and deserialization in methods that appended/read the to/from the xml and the attribute..
However I would inherit these classes from a base class that had the property IsDirty then you neednt worry!

Categories