I have below data structure to represent the tree
public class TreeNode : IEnumerable<TreeNode>
{
private readonly Dictionary<string, TreeNode> _children =
new Dictionary<string, TreeNode>();
public readonly string ID;
public TreeNode Parent { get; private set; }
public TreeNode()
{
this.ID = Guid.NewGuid().ToString();
}
public void AddChild(TreeNode tNode)
{
if (tNode.Parent != null)
{
tNode.Parent._children.Remove(tNode.ID);
}
tNode.Parent = this;
this._children.Add(tNode.ID, tNode);
}
public IEnumerator<TreeNode> GetEnumerator()
{
return this._children.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
I am trying to return this tree structure from wcf service.
[OperationContract]
TreeNode GetMenuItems();
and i get exception saying "Type 'TreeNode' is a recursive collection data contract which is not supported. Consider modifying the definition of collection 'TreeNode' to remove references to itself."
When i annotated the TreeNode calss with [DataContract(IsReference = true)], exception went away.
But when the treenode is received in the client side its children _children property is not coming populated and its always null.
Thanks,
Mallikarjun
By annotating _children with DataMember issue got resolved.
The problem I have involves a pretty complex class structure but I managed to summarize the gist of it in the following simpler example. I need to be able to serialize an object of class MyItem (including private property 'text') and subsequently deserialize it without having a parameter-less constructor available and without being able to create one because it would totally mess up the current logic.
class MyCollection:
#XmlRootElement(name="collection")
public class MyCollection {
public MyCollection() {
this.items = new ArrayList<MyItem>();
}
#XmlElement(name="item")
private List<MyItem> items;
public void addItem(String text) {
this.items.add(new MyItem(text));
}
}
class MyItem:
public class MyItem {
public MyItem(String text) {
this.text = text;
}
#XmlAttribute
private String text;
}
The first requirement (serialize MyItem including private property) is met out of the box and I get the following xml as a result:
<collection>
<item text="FIRST"/>
<item text="SECOND"/>
<item text="THIRD"/>
</collection>
In order to meet the second requirement I decorated class MyItem with attribute #XmlJavaTypeAdapter
#XmlJavaTypeAdapter(MyItemAdapter.class)
public class MyItem {
...
and introduced classes AdaptedMyItem
public class AdaptedMyItem {
private String text;
public void setText(String text) { this.text = text; }
#XmlAttribute
public String getText() { return this.text; }
}
and MyItemAdapter
public class MyItemAdapter extends XmlAdapter<AdaptedMyItem, MyItem> {
#Override
public MyItem unmarshal(AdaptedMyItem adaptedMyItem) throws Exception {
return new MyItem(adaptedMyItem.getText());
}
#Override
public AdaptedMyItem marshal(MyItem item) throws Exception {
AdaptedMyItem result = new AdaptedMyItem();
result.setText("???"); // CANNOT USE item.getText()
return result;
}
}
but this is where I get stuck because in method marshal I cannot access MyItem.text and so I cannot use the standard approach for dealing with immutable classes in JAXB.
Bottomline: I would like to use the class adapter mechanism only when deserializing (because I need to invoke a non-parameterless constructor) but not when serializing (because I cannot access private properties). Would that be possible?
I'm trying to make DataContract Serializer work with one of my class.
Here it is :
public class MyOwnObservableCollection<T> : ObservableCollection<T>, IDisposable
where T : IObjectWithChangeTracker, INotifyPropertyChanged
{
protected List<T> removedItems;
[DataMember]
public List<T> RemovedItems
{
get { return this.removedItems;}
set { this.removedItems = value;}
}
// Other code removed for simplification
// ...
//
}
It is important to understand that the RemovedItems list gets populated automatically when you remove an Item from the ObservableCollection.
Now serializing an instance of this class using the DataContractSerializer with one element in the removedItems list with the following code :
MyOwnObservableCollection<Test> _Test = new MyOwnObservableCollection<Test>();
DataContractSerializer dcs = new DataContractSerializer(typeof(MyOwnObservableCollection<Test>));
XmlWriterSettings settings = new XmlWriterSettings() { Indent = true };
string fileName = #"Test.xml";
Insurance atest = new Test();
atest.Name = #"sfdsfsdfsff";
_Test.Add(atest);
_Test.RemoveAt(0); // The Iitem in the ObservableCollection is moved to the RemovedItems List/
using (var w = XmlWriter.Create(fileName, settings))
{
dcs.WriteObject(w, _Test);
}
ends with nothing in the XML file :
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfTest xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="MyNameSpace" />
Why is this public property ignored ? What have I missed here ?
TIA.
The problem here is that your class is derived from a collection, and as such, the DataContractSerializer serializes only its items, but not any extra properties, as stated here: No properties when using CollectionDataContract.
A workaround would be using the original (inherited) collection as a property, rather than inheriting from it:
public class MyOwnObservableCollection<T> : IDisposable
where T : IObjectWithChangeTracker, INotifyPropertyChanged
{
readonly ObservableCollection<T> originalCollection = new ObservableCollection<T>();
protected List<T> removedItems = = new List<T>();
[DataMember]
public List<T> RemovedItems
{
get { return this.removedItems;}
set { this.removedItems = value;}
}
[DataMember]
public ObservableCollection<T> OriginalCollection
{
get { return this.originalCollection; }
}
// ...
}
I have a following problem. A customer requested a web service that returns data in following format:
<status>
<name1>Some name</name1>
...
</status>
But when an error occurs they want to get a following message:
<status>
<error>Error description</error>
</status>
I created a web service using WCF and in order to fulfill the requirements I defined a following service contract:
[ServiceContract]
public interface IPatronStatus
{
[OperationContract]
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
[WebGet(UriTemplate = "/service/status?user={unilogin}")]
StatusData GetPatronStatus(string unilogin);
}
And also defined a following base class:
[DataContract(Name="status")]
public class StatusData
{
}
And two derrived classes:
public class PatronStatusData : StatusData
{
private string _name;
[DataMember(Name = "name1", Order = 0)]
public string Name
{
get { return _name; }
set { _name = value; }
}
...
}
And:
public class UniLoginNotFoundError : StatusData
{
public UniLoginNotFoundError()
{ }
private string _description = "UniLoginNotFoundError";
[DataMember(Name = "error", Order = 0)]
public string Description
{
get
{
return _description;
}
}
}
The problem is that when I pull the data from the web service the data contract name ("status") and the names of the data members are ignored and the type's and properties' names are used.
Is it possible to use the custome names?
You should decorate both UniLoginNotFoundError and PatronStatusData with DataContract(Name="Something") to make this work. But you won't be allowed to set the same name ("status") for them.
In your particular case I'd better use single class with unused properties set to null.
[DataContract(Name="status")]
public class StatusData
{
private string _name;
private string _errorDescription = null;
[DataMember(Name = "name1", Order = 0, EmitDefaultValue=false)]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember(Name = "error", Order = 1, EmitDefaultValue=false)]
public string Description
{
get{ return _errorDescription ;}
set {_errorDescription =value ;}
}
...
}
Generally speaking, it's a mistake to want too much control over the XML generated by serializing a Data Contract. That's the trap of the XML Serializer. Define the contract in general terms, and have the clients just consume the result, which will generally be simple enough.
I am trying to access settings in my config file, which is a series of xml elements listed as such:
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
Now I want to access it. I have set up classes like so:
Public Class DatabaseConfigurationHandler
Inherits ConfigurationSection
<ConfigurationProperty("Databases", IsDefaultCollection:=True)> _
Public ReadOnly Property Databases() As DatabaseCollection
Get
Return CType(Me("Databases"), DatabaseCollection)
End Get
End Property
End Class
Public Class DatabaseCollection
Inherits ConfigurationElementCollection
Protected Overloads Overrides Function CreateNewElement() As ConfigurationElement
Return (New Database())
End Function
Protected Overloads Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object
Return (CType(element, Database).DatabaseName)
End Function
End Class
Public Class Database
Inherits ConfigurationElement
<ConfigurationProperty("name", IsKey:=True, IsRequired:=True)> _
Public Property DatabaseName() As String
Get
Return Me("name").ToString()
End Get
Set(ByVal Value As String)
Me("name") = Value
End Set
End Property
<ConfigurationProperty("value", IsRequired:=True)> _
Public Property DatabaseValue() As String
Get
Return Me("value").ToString()
End Get
Set(ByVal Value As String)
Me("value") = Value
End Set
End Property
End Class
I want to be able get the element by it's name and return the value but I can't see to do that:
Dim config As New DatabaseConfigurationHandler
config = System.Configuration.ConfigurationManager.GetSection("databases/database")
Return config.Databases("DatabaseOne")
Am I missing some code, what am I doing wrong? Any other errors in the above?
Thanks.
Here's a cut and paste from something very similar I did a few days ago.
Config:
<ListConfigurations>
<lists>
<add Name="blah" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
<add Name="blah2" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
</lists>
</ListConfigurations>
Config section C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace App
{
/// <summary>
/// Individual list configuration
/// </summary>
class ListConfiguration : ConfigurationElement
{
[ConfigurationProperty("Name", IsKey = true, IsRequired = true)]
public string Name
{
get { return (string)this["Name"]; }
}
[ConfigurationProperty("EndpointConfigurationName", IsRequired = true)]
public string EndpointConfigurationName
{
get { return (string)this["EndpointConfigurationName"]; }
}
[ConfigurationProperty("ListName", IsRequired = true)]
public string ListName
{
get { return (string)this["ListName"]; }
}
[ConfigurationProperty("ConnectionString", IsRequired = true)]
public string ConnectionString
{
get { return (string)this["ConnectionString"]; }
}
[ConfigurationProperty("TableName", IsRequired = true)]
public string TableName
{
get { return (string)this["TableName"]; }
}
[ConfigurationProperty("FieldsCsv", IsRequired = true)]
public string FieldsCsv
{
get { return (string)this["FieldsCsv"]; }
}
[ConfigurationProperty("DbFieldsCsv", IsRequired = true)]
public string DbFieldsCsv
{
get { return (string)this["DbFieldsCsv"]; }
}
}
/// <summary>
/// Collection of list configs
/// </summary>
class ListConfigurationCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ListConfiguration();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ListConfiguration)element).Name;
}
}
/// <summary>
/// Config section
/// </summary>
class ListConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("lists")]
public ListConfigurationCollection Lists
{
get { return (ListConfigurationCollection)this["lists"]; }
}
}
}
And the code to pick it up from the main app:
ListConfigurationSection configSection = null;
try
{
configSection = ConfigurationManager.GetSection("ListConfigurations") as ListConfigurationSection;
}
catch (System.Configuration.ConfigurationErrorsException)
{
}
There isn't any good reason to design this kind of stuff by hand anymore. Rather, you should be using the Configuration Section Designer on CodePlex:
http://csd.codeplex.com/
Once installed, you can just add a new item to your project (a configuration section designer) and then add the elements and the constraints. I've found it VERY easy to use, and I will probably never write a piece of code for configuration files again.
You can use this configuration handler instead.. It will work for ALL custom configuration sections
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent,
object configContext, XmlNode section)
{
if (section == null)
throw new ArgumentNullException("section",
"Invalid or missing configuration section " +
"provided to XmlConfigurator");
XPathNavigator xNav = section.CreateNavigator();
if (xNav == null)
throw new ApplicationException(
"Unable to create XPath Navigator");
Type sectionType = Type.GetType((string)
(xNav).Evaluate("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}
Your config file then has to include a reference to the type that the represents the root element
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
A sample config file might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
<ConnCompanys>
<ConnCompany companyName="CompanyNameHere">
<ConnApps>
<ConnApp applicationName="Athena" vendorName="Oracle" >
<ConnSpecs>
<ConnSpec environments="DEV"
serverName="Athena"
port="1521"
catalog="DatabaseName"
logon="MyUserName"
password="%%552355%8234^kNfllceHGp55X5g==" />
<!-- etc...
And you will need to define the classes that each xml element maps to... using the appropriate XmlSerialization attributes ...
[XmlRoot("ConnectionConfig")]
public class ConnectionConfig
{
private ConnCompanys comps;
[XmlArrayItem(ElementName = "ConnCompany")]
public ConnCompanys ConnCompanys
{
get { return comps; }
set { comps = value; }
}
public ConnApp this[string CompanyName, string AppName]
{ get { return ConnCompanys[CompanyName][AppName]; } }
public ConnSpec this[string CompanyName, string AppName, APPENV env]
{
get
{
return ConnCompanys[CompanyName][AppName, env];
}
}
}
public class ConnCompanys : List<ConnCompany>
{
public ConnCompany this[string companyName]
{
get
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return comp;
return null;
}
}
public bool Contains(string companyName)
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return true;
return false;
}
}
public class ConnCompany
{
#region private state fields
private string compNm;
private ConnApps apps;
#endregion private state fields
#region public properties
[XmlAttribute(DataType = "string", AttributeName = "companyName")]
public string CompanyName
{
get { return compNm; }
set { compNm = value; }
}
[XmlArrayItem(ElementName = "ConnApp")]
public ConnApps ConnApps
{
get { return apps; }
set { apps = value; }
}
#endregion public properties
#region indexers
public ConnApp this[string applicationName]
{ get { return ConnApps[applicationName]; } }
public ConnSpec this[string applicationName, APPENV environment]
{
get
{
foreach (ConnSpec con in this[applicationName].ConnSpecs)
if (con.Environment == environment)
return con;
return null;
}
}
#endregion indexers
}
etc...
My VB isn't up to much sorry but here's how you do it in C#.
C#
public class AppState : IConfigurationSectionHandler
{
static AppState()
{
xmlNode myConfigNode = (XmlNode)ConfigurationManager.GetSection("databases");
}
public object Create(object parent, object context, XmlNode configSection) {
return configSection;
}
}
App.Config
<configuration>
<configSections>
<section name="databases" type="MyAssembly.AppState, MyAssembly" />
</configSections>
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
</configuration>
You might be interested in using a ConfigurationElementCollection where T is the type of the child ConfigurationElements. You can find sample code in C# at http://devpinoy.org/blogs/jakelite/archive/2009/01/10/iconfigurationsectionhandler-is-dead-long-live-iconfigurationsectionhandler.aspx
Cheers!
There's a nice simple way of doing this demonstrated here also:
codeproject.com/KB/XML/xml_config_section.aspx