1

The field is used only during the serialization / deserialization process but I would like to immediately encapsulate it and hide from the class.

Is it possible?

dbc
  • 104,963
  • 20
  • 228
  • 340
Revious
  • 7,816
  • 31
  • 98
  • 147
  • 2
    https://stackoverflow.com/help/mcve Please show us an example class. – mjwills Nov 30 '17 at 11:08
  • 1
    No, that´s not possible, as you can only serialize fields and properties that are public and thus are visible to the outside. However you can give them different names in your class than within the xml. Anyway it sounds strange to even think to need this. You may create some simple DTO that holds the data to be read(written from/to the xml and have another class that exposes your actual *logic*. – MakePeaceGreatAgain Nov 30 '17 at 11:08
  • 1
    What do you want to do *actually*. Seems like your actual problem, which you´re trying to solve, is something completey different. – MakePeaceGreatAgain Nov 30 '17 at 11:10
  • Do you want to hide it within the class itself or just from other classes? – MakePeaceGreatAgain Nov 30 '17 at 11:16
  • 2
    Do you accept using two classes (one for the serialization, one for the "cleaner" version where the serializer fields are no longer present)? I think this is going to be the only way to achieve it, since serialization requires public properties to work, so you can't just hide the properties. – Flater Nov 30 '17 at 11:27
  • @HimBromBeere: it would be enough to hide the field to the other classes – Revious Nov 30 '17 at 11:38
  • 1
    Are you using `XmlSerializer`? If so, as the other answers state, you cannot deserialize to a private property. But if you are constructing the `XmlSerializer` directly (rather than using a serializer constructed by a framework), you could do something in the `UnknownElement` event. See e.g. https://stackoverflow.com/a/40244396 or https://stackoverflow.com/a/24708143/3744182. Would this approach work for you? – dbc Nov 30 '17 at 23:06

3 Answers3

2

Its not possible with XML-Serialization in C# , if you want to do like that than you should make use of DataContractSerialization, It allows this kind of functionality i.e. you can serialize private field of you object.

Below is possible with DataContractSerialization, I hope you like to try out

[DataContract]
class Person
{
    [DataMember]
    public string m_name;

    [DataMember]
    private int m_age;
}

This what I tried when I was learning XML to Linq , and this is wired solution but if you want to try , here i created xml string by using xml to linq

here is my article : Object to XML using LINQ or XmlSerializer

Note : here code field of product class is private field but still you can generate xml string

using System.Collections.Generic;
using System.Xml.Linq;
using System.Linq;

class Program
{
     public class Product
        {
            public Product()
            { }

            public Product(string name,int code, List<productType> types)
            {
                this.Name = name;
                this.Code = code;
                this.types = types;
            }

            public string Name { get; set; }
            private int Code { get; set; }
            public List<productType> types { get; set; }

            public string Serialize(List<Product> products)
            {
                XElement productSer = new XElement("Products",
               from c in products
               orderby c.Code
               select new XElement("product",
                   new XElement("Code", c.Code),
                   new XElement("Name", c.Name),
                   new XElement("Types", (from x in c.types
                                          orderby x.type//descending 
                                           select new XElement("Type", x.type))
               ))
          );
                return productSer.ToString();
            }
        }
        public class productType
        {
            public string type { get; set; }
        }

 public static void Main()
    {    
        List<productType> typ = new List<productType>();
        typ.Add((new productType() { type = "Type1" }));
        typ.Add((new productType() { type = "Type2" }));
        typ.Add((new productType() { type = "Type3" }));

        List<Product> products =new List<Product>() { new Product ( "apple", 9,typ) ,
                      new Product ("orange", 4,typ   ),
                      new Product ("apple", 9 ,typ),
                      new Product ("lemon", 9,typ ) };

   Console.WriteLine(new Product().Serialize(products));
        Console.ReadLine();
 }
}
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
  • 1
    OP doesn´t want to serialize private fields, but use a field only for serialization, while not making it accesable anywhere else in code. – MakePeaceGreatAgain Nov 30 '17 at 11:11
  • why -1 ?? i want to ask whatever he is asking is it possible with XML-Serialization , its better to provide other way , where it possible – Pranay Rana Nov 30 '17 at 11:12
  • @HimBromBeere - I know what he asked and its not possible , I provided answer with the name of class / type of Serialization who can support that sutff.....Read my answer correctly I wrote its not possible with XMLSerialization in first line its self – Pranay Rana Nov 30 '17 at 11:13
  • I doubt DataContractSerializer can hide a member from your code while exposing it to the serializer. But if I´m whrong, you should show some example, which also would help OP. – MakePeaceGreatAgain Nov 30 '17 at 11:14
  • @HimBromBeere - its better you can comment on answer ...but simply putting -1 its discouraging people in providing solution ....if given wrong information than it ok – Pranay Rana Nov 30 '17 at 11:14
  • @HimBromBeere - DataContractSerialization, allows you to serialize private fields ...its written on msdn and i tried same when designed WEBAPI – Pranay Rana Nov 30 '17 at 11:15
  • 1
    `DataContractSerializer` does indeed support non-public members, but it would **not** be my choice for anything where I care about the actual xml. It is far less versatile (in terms of xml) than `XmlSerializer` – Marc Gravell Nov 30 '17 at 11:25
  • 1
    Author doesn't say anywhere that he wants to use `XmlSerializer` specifically, so this answer looks valid to me. – Evk Nov 30 '17 at 11:29
2

Basically, no.

XmlSerializer only works with public members, so you can't make it internal or private. You can add some attributes to make it less glaring especially in UIs that data-bind:

[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public int Foo {get; set; }

but that only masks it. You could also look at IXmlSerializable, but that is a horrible API and most implementations of it are simply buggy - I do not recommend implementing this interface.

But: best practice is that whenever serialization requirements conflict with your model's design: create a dedicated DTO model - one that matches perfectly your chosen serialization library and exists purely for that purpose. And then map between the two. Then you don't have to compromise.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

Assuming you are using XmlSerializer, then only public fields and properties can be serialized, as explained in Troubleshooting Common Problems with the XmlSerializer:

The serializer examines all public fields and properties of the Type to learn about which types an instance references at runtime. It then proceeds to create C# code for a set of classes to handle serialization and deserialization using the classes in the System.CodeDOM namespace.

So, what are your options? If you are able to construct your XmlSerializer directly, you could make use of the XmlSerializer.UnknownElement event to forward the unknown elements to the object being deserialized for processing.

First, define the following attribute and extension methods:

[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = false)]
public class XmlUnknownElementEventHandlerAttribute : System.Attribute
{
}

public static partial class XmlSerializationHelper
{
    public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
    {
        serial = serial ?? new XmlSerializer(typeof(T));
        serial.UnknownElement += UnknownXmlElementEventHandler;
        using (StringReader reader = new StringReader(xmlString))
        {
            return (T)serial.Deserialize(reader);
        }
    }

    public static void UnknownXmlElementEventHandler(object sender, XmlElementEventArgs e)
    {
        var obj = e.ObjectBeingDeserialized;

        foreach (var method in obj.GetType().BaseTypesAndSelf()
            .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
            .Where(m => Attribute.IsDefined(m, typeof(XmlUnknownElementEventHandlerAttribute))))
        {
            method.Invoke(obj, BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { sender, e }, null);
        }
    }
}

public static class TypeExtensions
{
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
    {
        while (type != null)
        {
            yield return type;
            type = type.BaseType;
        }
    }
}

Next, say you have some class like:

public partial class MyClass
{
    public string MyValue { get; set; }
}

And some XML containing an element that needs to be post-processed and converted into the current model, e.g. <OldValue>:

<MyClass><OldValue>Hello</OldValue></MyClass>

Then add a method to MyClass that:

  • Can be private or internal (in full trust) or public;

  • Has the same signature as XmlElementEventHandler;

  • Is marked with your custom attribute [XmlUnknownElementEventHandler];

  • Performs the necessary post-processing on the old element.

And now the unknown element will be forwarded to it when using a serializer constructed by XmlSerializationHelper.LoadFromXml().

E.g., your method might look like:

public partial class MyClass
{
    [XmlUnknownElementEventHandler]
    void HandleOldElement(object sender, XmlElementEventArgs e)
    {
        if (e.Element.Name == "OldValue")
        {
            Debug.WriteLine("{0}: processed property {1} with value {2}", this, e.Element.Name, e.Element.OuterXml);
            MyValue = "Old value was: " + e.Element.InnerText;
        }
    }
}

And you would deserialize as follows:

var model = xmlString.LoadFromXml<MyClass>();

One advantage of this solution is that it doesn't modify the XSD generated for your types in any way.

Sample fiddle. (Note that, because the dotnetfiddle code executes in partial trust, the handlers must be public. That's not necessary in full trust.)

dbc
  • 104,963
  • 20
  • 228
  • 340