XML Serialization Circular Reference - c#

In my application I use the following code to serialze objects:
private static string Serialize(Type type, object objectToSerialize)
{
StringBuilder builder = new StringBuilder();
using (TextWriter writer = new StringWriter(builder))
{
XmlSerializer serializer = new XmlSerializer(type);
serializer.Serialize(writer, objectToSerialize);
}
return builder.ToString();
}
This code just worked fine until now.
We've introduced a new class which looks like:
[Serializable]
public class Restriction
{
public string Id { get; set; }
public ResticType Type { get; set; }
public Restriction Parent { get; set; }
public List<Restriction> Children { get; set; }
}
If I try to serialize it I get the following exception:
A circular reference was detected while serializing an object of type Restriction
I already found out, that this occures because of the Parent and the Children which are also of type Restriction
I've already tried to set the Parent-Property to NonSerialized but this doesn't work.
Unfortunately I can not change the code for serialization...
What can I do to serialize this class?
Actual my only idea is to implement IXmlSerializable in my Restriction-class and do the reading and writing of the xml by my own. I hope there is another way...

I've already tried to set the Parent-Property to NonSerialized but this doesn't work.
NonSerialized is for binary serialization. Use XmlIgnore instead.
Note that you'll have to manually restore the Parent property after deserialization:
void RestoreParentRelationship(Restriction restriction)
{
foreach (var child in restriction.Children)
child.Parent = restriction;
}

Related

Is there a way to retain XML Attributes when serializing part of a class?

I am attempting to serialize just part of a class. I've added XML attributes to the class members so that the generated XML tags are correctly named to match a spec regardless of what my properties are named. This works fine when serializing the main class. However, if I just want to serialize part of the class, I lose the XML attributes and the names go back to their defaults. Is there a way to retain the XML attributes when serializing just part of a class?
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
When I serialize the entire class, I get this (which is exactly as I would expect):
<someConfiguration>
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
</someConfiguration>
If I attempt to just serialize the 'Bugs' part of the class, I get this (note the XML attributes that change the tag names are all ignored):
<ArrayOfString>
<string>Bug1</string>
<string>Bug2</string>
<string>Bug3</string>
</ArrayOfString>
I need to get this:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>
How do I get the partial class to serialize with the above tags?
Or better yet, is there a way to specify tag names when serializing a simple List<object>. So that you can specify the tag used for the list instead of it using <ArrayOfobject> and specify the tag used for the array items instead of <object>?
is there a way to specify tag names when serializing a simple List.
In general, depending on the exact scenario, it may be possible to get this to work. See MSDN's How to: Specify an Alternate Element Name for an XML Stream. The example there involves overriding serialization of a specific field, but it may be possible to use the same technique to override whole type names as well.
But it seems like an awful lot of trouble to me. Instead, why not just handle the serialization explicitly:
private static string SerializeByLinqAndToString<T>(
List<T> data, string rootName, string elementName)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(s => new XElement(elementName, s))));
return SaveXmlToString(document);
}
private static string SaveXmlToString(XDocument document)
{
StringBuilder sb = new StringBuilder();
using (XmlWriter xmlWriter = XmlWriter.Create(sb,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
document.Save(xmlWriter);
}
return sb.ToString();
}
Call like this:
SomeConfiguration config = ...; // initialize as desired
string result = SerializeByLinq(config.Bugs, "bug", "bugs");
The above works only with a list of strings, or a list of types where the element content can be simply the result of calling ToString() on the instance of the type.
Using the full-blown serialization features in .NET might be worthwhile when dealing with complex types, but if all you've got is a simple list of strings, the LINQ-to-XML feature is very handy.
If you do have more complex types, you can transform each list element into an XElement for the DOM and serialize that:
private static string SerializeByLinq<T>(
List<T> data, string rootName, string elementName = null)
{
XDocument document = new XDocument(
new XElement(rootName, data.Select(t =>
ElementFromText(SerializeObject(t), elementName)
)));
return SaveXmlToString(document);
}
private static XElement ElementFromText(string xml, string name = null)
{
StringReader reader = new StringReader(xml);
XElement result = XElement.Load(reader);
if (!string.IsNullOrEmpty(name))
{
result.Name = name;
}
return result;
}
private static string SerializeObject<T>(T o)
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
StringWriter textWriter = new StringWriter();
using (XmlWriter writer = XmlWriter.Create(textWriter,
new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }))
{
xmlSerializer.Serialize(writer, o,
new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty}));
}
return textWriter.ToString();
}
In this second example, you can omit the element name for the child, and it will just use whatever the type's set up to use already (e.g. the type name, or whatever [XmlRoot] is set to).
Just throwing this out there, you could wrap the List<> inside a custom class:
[XmlRoot("config")]
public class SomeConfiguration
{
[XmlElement("bugs")]
public BugList Bugs { get; set; }
[XmlElement("trees")]
public TreeList Trees { get; set; }
}
[XmlRoot("bugs")]
public class BugList
{
[XmlElement("bug")]
public List<string> Items = new List<string>();
}
[XmlRoot("trees")]
public class TreeList
{
[XmlElement("tree")]
public List<string> Items = new List<string>();
}
That will now allow you to serialize the individual Lists and they'll be rooted as you'd expect.
void Main()
{
var config = new SomeConfiguration
{
Bugs = new BugList { Items = { "Bug1", "Bug2" } },
Trees = new TreeList { Items = { "Tree1", "Tree2" } }
};
// Your config will work as normal.
Debug.WriteLine(ToXml(config)); // <config> <bugs>.. <trees>..</config>
// Your collections are now root-ed properly.
Debug.WriteLine(ToXml(config.Bugs)); // <bugs><bug>Bug1</bug><bug>Bug2</bug></bugs>
Debug.WriteLine(ToXml(config.Trees)); // <trees><tree>Tree1</tree><tree>Tree2</tree></trees>
}
public string ToXml<T>(T obj)
{
var ser = new XmlSerializer(typeof(T));
var emptyNs = new XmlSerializerNamespaces();
emptyNs.Add("","");
using (var stream = new MemoryStream())
{
ser.Serialize(stream, obj, emptyNs);
return Encoding.ASCII.GetString(stream.ToArray());
}
}
Found a 'work-around' way to do it.. Instead of putting XMLArray and XMLArrayList attributes above the List<>:
[XmlRoot ("someConfiguration")]
public class SomeConfiguration
{
[XmlArray("bugs")]
[XmlArrayItem("bug")]
public List<string> Bugs { get; set; }
}
Put an XmlElement attribute on the list which will specify the tag to be used for each element and not have a tag wrapping the list. Your class tag will in effect do that for you.
[XmlRoot ("bugs")]
public class SomeConfiguration
{
[XmlElement("bug")]
public List<string> Bugs { get; set; }
}
When you serialize the above, you will end up with:
<bugs>
<bug>Bug1</bug>
<bug>Bug2</bug>
<bug>Bug3</bug>
</bugs>

Serialize a list of derived list [duplicate]

This question already has answers here:
How do I get json.net to serialize members of a class deriving from List<T>?
(3 answers)
Closed 6 years ago.
I can't find a way to serialize with JSON.NET a list of derived lists, and I'm starting to wonder if it's even possible. Let me explain the situation with some code.
I've created a new class which is derived from a list of a specific object (tried first with a generic one) and added a string property NomGroupe:
public class GroupeActes : List<Acte>
{
public string NomGroupe { get; set; }
public GroupeActes(string nom, List<Acte> liste)
{
NomGroupe = nom;
foreach (var acte in liste)
{
this.Add(acte);
}
}
}
Then, in my code, I've declared a list of this class (List<GroupeActes> listOfGroupeActes) and I fill it with data. For the serialization, I use this code:
JsonSerializer serializer = new JsonSerializer();
serializer.TypeNameHandling = TypeNameHandling.All;
using (StreamWriter sw = new StreamWriter(#"listOfGroupeActes.json"))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, listOfGroupeActes);
}
I've tried with and without the TypeNameHandling.All parameter and with several combination of Json.net properties and even with DataContract/DataMember.
So far, I only managed to get in my json file either the data of each nested List<Acte> without the NomGroupe property, or the other way around. But not both, which is what I'd like to have.
Two questions then:
Is it even possible?
If yes, how can I do it?
Thanks for your help!
You don't want to inherit from List<T>.
Create a list property instead:
public class GroupeActes
{
public List<Acte> Actes { get; set; }
public string NomGroupe { get; set; }
public GroupeActes(string nom, List<Acte> liste)
{
NomGroupe = nom;
Actes.AddRange(acte);
}
}
Lists (and other collection types) get special treatment while serializing. You don't want the collection type's public properties (such as Capacity and Count) in your output, so the property you added through inheritance won't be serialized either.
A collection is serialized like this:
if o is IEnumerable
foreach object s in o
serialize o
So the serializer won't even look at your enumerable's properties.
Try to use Newtonsoft Json.NET
string itemToSend = JsonConvert.SerializeObject(dataModel);

How to exclude class fields from serialization by XmlElement name?

Say we have a class like:
using System;
using System.Xml.Serialization;
namespace XmlEntities {
[XmlRoot("Agent")]
public class RootClass {
private string element_description;
[XmlElement("Name")]
public string Name{ get; set; }
}
[XmlElement("Surname")]
public string Surname{ get; set; }
}
}
And we want to serialize into xml only Name XmlElement. How to limit that at serialization?
Have you encountered the [XmlIgnore] attribute? Adding it to members of your class will exclude them from serialization.
So, for example, you can replace [XmlElement("Surname")] with [XmlIgnore], and then your serialized agent will look like this:
<Agent>
<Name>John Doe</Name>
</Agent>
Alternately, if all you really want is just <Name>John Doe</Name>, you could write a wrapper class:
[XmlRoot("Name")]
public class NameElement
{
[XmlText]
public string Name { get; set; }
}
* EDIT *
While it's possible to generate such wrappers at runtime, it's difficult, inefficient, and not very practical.
To do so, I guess you could reflectively examine your object and find the properties you want (root.GetType().GetProperties().Where(p => /* your logic here */)), and use System.Reflection.Emit to generate the appropriate class. While possible, it's not reasonable - it'd be a huge amount of code relative to your actual logic, and you could easily destabilize the runtime and/or leak memory.
A better way to achieve the dynamicism you want is to forego System.Xml.Serialization and use System.Xml.Linq. This would require you to write code that built up the xml yourself, but it's super easy:
public XElement ConvertToXml(RootClass root)
{
return new XElement("Name", root.Name);
}
You can write an XElement to any stream using the element.Save(Stream) instance method on XElement.
Read more about Linq to XML at MSDN
If you want to ignore the element at runtime, at serialization, you can use XmlAttributeOverrides:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("Surname"));
overrides.Add(typeof(RootClass), "Surname", attribs);
XmlSerializer ser = new XmlSerializer(typeof(RootClass), overrides);
RootClass agent = new RootClass();
agent.Name = "Marc";
agent.Surname = "Jobs";
ser.Serialize(Console.Out, agent);

c# JavaScriptConverter - how to deserialize custom property?

I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.
e.g.
public class ContentItemViewModel
{
public string CssClass { get; set; }
public MyCustomClass PropertyB { get; set; }
}
the simple property (CssClass) will deserialize with:
var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);
But PropertyB gets an error...
We added a JavaScriptConverter:
ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});
But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.
We've got a current solution which looks something like this:
class ContentItemViewModelConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var cssClass = GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...
var propertyB= GetString(dictionary, "propertyB");
return new ContentItemViewModel{ CssClass = cssClass ,
PropertyB = new MyCustomClass(propertyB)}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new Exception("Only does the Deserialize");
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new List<Type>
{
typeof(ContentItemViewModel)
};
}
}
}
But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....
Is there a way to Deserialize JUST PropertyB of type MyCustomClass?
Thanks for your help!
Have you considered using DatacontractJsonSerializer
[DataContract]
public class MyCustomClass
{
[DataMember]
public string foobar { get; set; }
}
[DataContract]
public class ContentItemViewModel
{
[DataMember]
public string CssClass { get; set; }
[DataMember]
public MyCustomClass PropertyB { get; set; }
}
class Program
{
static void Main(string[] args)
{
ContentItemViewModel model = new ContentItemViewModel();
model.CssClass = "StackOver";
model.PropertyB = new MyCustomClass();
model.PropertyB.foobar = "Flow";
//Create a stream to serialize the object to.
MemoryStream ms = new MemoryStream();
// Serializer the User object to the stream.
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
ser.WriteObject(ms, model);
byte[] json = ms.ToArray();
ms.Close();
string s= Encoding.UTF8.GetString(json, 0, json.Length);
Console.ReadLine();
}
}
Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.
For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.
On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.
var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
// set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);
A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.
Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.
If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.
If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:
The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service’s method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.
Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.

Can I apply an attribute to an inherited member?

Suppose I have the following (trivially simple) base class:
public class Simple
{
public string Value { get; set; }
}
I now want to do the following:
public class PathValue : Simple
{
[XmlAttribute("path")]
public string Value { get; set; }
}
public class ObjectValue : Simple
{
[XmlAttribute("object")]
public string Value { get; set; }
}
But without actually redefining the property. I want to apply attributes to members of the base class. Is this possible?
The real problem is that in my serialization mechanism from/to XML (which works brilliantly btw), I find a lot of similar elements where only the names of the attributes differ (they're not consistent, and I don't control the format). Right now I need to create a different class for every such element, whereas they're like 100% the same (apart from the attributes).
I don't think it's possible, but you might never know.
UPDATE:
I tried Marc's approach, but to no avail:
public class Document
{
public PathValue Path;
public ObjectValue Object;
}
class Program
{
static void Main(string[] args)
{
var doc = new Document()
{
Path = new PathValue() { Value = "some path" },
Object = new ObjectValue() { Value = "some object" }
};
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(PathValue), "Value", new XmlAttributes() { XmlAttribute = new XmlAttributeAttribute("path") });
overrides.Add(typeof(ObjectValue), "Value", new XmlAttributes() { XmlAttribute = new XmlAttributeAttribute("object") });
XmlSerializer serializer = new XmlSerializer(typeof(Document), overrides);
serializer.Serialize(Console.Out, doc);
Console.WriteLine();
Console.ReadLine();
}
}
...doesn't do the trick.
I'm going to answer this question myself, so that I can accept this answer. I don't like the answer, but I suppose it's the only valid answer.
The answer is: No, you can't do it.
Could you perhaps use the overload XmlSerializer constructor that lets you pass in the attributes to apply at runtime? Then you don't have to worry about it...
caveat: you want to cache the serializer instance and re-use it; otherwise (with the complex constructors) it does dynamic type generation each time.
Example:
using System;
using System.Xml.Serialization;
public class Simple {
public string Value { get; set; }
static void Main() {
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Simple), "Value", new XmlAttributes {
XmlAttribute = new XmlAttributeAttribute("path")
});
XmlSerializer pathSerializer = new XmlSerializer(
typeof(Simple), overrides);
// cache and re-use pathSerializer!!!
Simple obj = new Simple();
obj.Value = "abc";
pathSerializer.Serialize(Console.Out, obj);
}
}
Output:
<Simple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" path="abc" />
How about this:
public class Simple
{
[XmlIgnore]
public string Value { get; set; }
}
public class PathValue : Simple
{
[XmlAttribute("path")]
public string Path {
get { return base.Value != null ? base.Value : null; }
set { base.Value = value != null ? value : null; }
}
}
public class ObjectValue : Simple
{
[XmlAttribute("object")]
public string Object {
get { return base.Value != null ? base.Value : null; }
set { base.Value = value != null ? value : null; }
}
}
This is the same technique used to serialize an unserializable type like a Uri that takes a serializable type in the constructor.
You are probably aware of this, but as an idea (although the code structure would completely change in that case):
One way would be to serialize the base class as a collection of name-value pairs, using custom serialization (there is also XDocument and similar helpful stuff to make it easier). Although it doesn't enforce type safety, it would spare you from doing lots of manual work.
I also prefer going for custom serialization because it allows a wider range of possibilities (serializing immutable classes, for example). XmlSerializer is also really nasty sometimes (e.g. I hate adding the "MyFieldSpecified" property to create optional attributes).
Perhaps you can mark the base class property with a common mapping, than you only override the property in inherited classes where it should be different. At least you would save some overriding.

Categories

Resources