My "Location" object isnt getting serialized in my WCF datacontract, however, all other variables are being set properly. When I try to output a variable in the location object I get the "Object reference not set to an instance of an object" error
My DataContract:
[DataContract(Namespace = "")]
public class CalcRequest : BaseRequest
{
[DataMember(Name = "Products")]
public List<Product> products;
[DataMember(Name = "Location")]
public Location location;
[DataMember(Name = "ShippingMethod")]
public string shippingMethod;
[DataMember(Name = "SystemPromotionCode")]
public string systemPromotionCode;
[DataMember(Name = "UserPromotionCode")]
public string userPromotionCode;
}
The "Location" object:
[DataContract(Name = "Location", Namespace = "")]
public class Location
{
public Location()
{
// do nothing
}
[DataMember(Name = "Country")]
public string country;
[DataMember(Name = "StateProvince")]
public string stateProvince;
[DataMember(Name = "PostalCode")]
public string postalCode;
}
my XML request (version, msgtype, processorID, and customerid are in my "BaseRequest"):
<root>
<Version>1.0</Version>
<MsgType>type</MsgType>
<ProcessorId>28000</ProcessorId>
<CustomerId>28000</CustomerId>
<Products>
<Product>
<SKU>1</SKU>
<Price>2999</Price>
<ProductName>name1</ProductName>
<Quantity>1</Quantity>
</Product>
<Product>
<SKU>2</SKU>
<Price>1999</Price>
<ProductName>name2</ProductName>
<Quantity>1</Quantity>
</Product>
</Products>
<Location>
<Country>US</Country>
<StateProvince>OH</StateProvince>
<PostalCode>44060</PostalCode>
</Location>
<ShippingMethod>USPS-NextDay</ShippingMethod>
<SystemPromotionCode>CD1244578</SystemPromotionCode>
<UserPromotionCode>2FDGRR</UserPromotionCode>
</root>
... Not sure why this isn't working... any help would be appreciated.
I don't understand what you think is missing, really....
(stuff deleted - not relevant)
UPDATE: to make sure the order of the elements in the XML is correct and interpreted in the right order, you might want to add Order=xxx statement to the data member attributes-
Otherwise, the data contract serializer will serialize (and deserialize) in alphabetical order (other than the XmlSerializer which serializes in the order the fields appear).
Alphabetical order is case-sensitive,i.e. any upper case characters are considered before any low-case characters.
If you have multiple elements of the same order (that's not a problem), then they'll be serialized alphabetically within their order (e.g. all elements of Order=1 will be serialized in an alphabetical fashion - then all elements with Order=2 and so on).
For derived classes, properties of base class will be serialized first(in alphabetical order) and properties of derived class later(also in alphabetical order).
Related
I am creating a REST service using asp.net mvc4 web api. My service returns xml as output.
I want to change some apsects of the xml response including:
- The xml root node
- Add namespaces
- Remove xsi:nil in the xml
I am using a datacontext file (Linq to sql dbml file) in my model and not a user defined class.
I have read from this link http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization that I can use DataContract to do so but don't know how to implement that in my case.
I do not want to use message handlers since this will require loading the entire xml in a string and might affect performance regarding that the xml output returned might be big
please help...
This example should be helpful:
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person : IExtensibleDataObject
{
// To implement the IExtensibleDataObject interface, you must also
// implement the ExtensionData property.
private ExtensionDataObject extensionDataObjectValue;
public ExtensionDataObject ExtensionData
{
get
{
return extensionDataObjectValue;
}
set
{
extensionDataObjectValue = value;
}
}
[DataMember(Name = "CustName")]
internal string Name;
[DataMember(Name = "CustID")]
internal int ID;
public Person(string newName, int newID)
{
Name = newName;
ID = newID;
}
}
You can read more on MSDN
i wrote one method for getting details.in rest client getting response like this.
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"><Meetings>
<Meeting>
<Id>1</Id>
<Name>Meeting1</Name>
<Place>SR Nagar</Place>
<Time>12/4/12 12:30pm</Time>
</Meeting>
<Meeting>
<Id>2</Id>
<Name>Meeting2</Name>
<Place>Begumpet</Place>
<Time>12/4/12 1:00pm</Time>
</Meeting>
</Meetings>
</string>
in rendered html getting proper.
<Meetings> <Meeting> <Id>1</Id> <Name>Meeting1</Name> <Place>SR Nagar</Place> <Time>12/4/12 12:30pm</Time> </Meeting> <Meeting> <Id>2</Id> <Name>Meeting2</Name> <Place>Begumpet</Place> <Time>12/4/12 1:00pm</Time> </Meeting> </Meetings>
How to handle it in code in wcf to avoid <
Your service operation returns string and you are writing XML to that string. It will always look like that and browser shows it correctly only because it hides string tag and unescapes the content.
To return real XML you must not use string as return value. Try to use for example XElement.
In your [OperationContract] method, instead of returning a string, return an array of Meeting objects. The Meeting class should contain the properties you'd like to return:
[DataContract]
public class Meeting
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Place { get; set; }
// etc...
}
If you're using .NET 4.0 and up, the [DataContract] and [DataMember] attributes are not required.
I'm a little inexperienced with the DataContract paradigm, and I'm running into a deserialization problem. I have a field that's a string, but it contains xml and it's not being deserialized correctly. I have a feeling that it's because the DCS is treating it as input to the serializer and not as an opaque string object.
Is there some way to mark a DataMember in code to say "This thing is a string, don't treat its contents as xml" similar to XmlIgnore?
Thanks!
Well, the equivalent to [XmlIgnore] is just not putting a [DataMember] on your property/field - if you're decorating everything with [DataMember] otherwise.
But I don't see how you could tell the DataContractSerializer to treat the property as an opaque string and nothing else.
You could try to have a separate property which adds <![CDATA[ before and ]]> after your content string, and serialize that property instead of your raw XML property (by decorating that new property with the [DataMember] attribute).
Something like this:
public string XmlContent { get; set; }
[DataMember]
public string XmlContentSafe
{
get { return "<![CDATA[" + XmlContent + "]]>"; }
}
Maybe that way you can trick the DCS ? (never tried it myself - just guessing....)
Turns out the easiest way to do this was just to cast the xml field coming from sql server to a varchar(max) when retrieving it from the database.
CAST(CONVERT(XML,[RawXml],0) AS VARCHAR(MAX)) AS RawXml
In this case, the serializer seems to be ignoring it as desired.
Thanks for the help though!
There is an easy way to do, just declare the property with raw XML as XmlElement
[DataMember]
public XmlElement RawXML { private get; set; }
I'm using v2.1 of NHibernate.dll and NHibernate.Mappings.Attributes v2.1 in a project.
When I run the code further below, I get the following exception, and will be grateful for any pointers. On the same project, if I remove the attributes and use xml mapping files, it works fine.
NHibernate.MappingException was unhandled
Message="Could not compile the mapping document:
DomainModel.hbm.xml"
Source="NHibernate"
InnerException: System.NullReferenceException
Message="Object reference not set to an instance of an object."
Source="NHibernate"
StackTrace:
at NHibernate.Cfg.XmlHbmBinding.ClassBinder.BindClass
(XmlNode node, PersistentClass model)
at NHibernate.Cfg.XmlHbmBinding.RootClassBinder.Bind
(XmlNode node, HbmClass classSchema)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.AddRootClasses(XmlNode
parentNode)
at NHibernate.Cfg.XmlHbmBinding.MappingRootBinder.Bind(XmlNode node)
at NHibernate.Cfg.Configuration.AddValidatedDocument(NamedXmlDocument doc)
InnerException:
I have a contact class as follows (Domain class has just one method, no properties):
[NHibernate.Mapping.Attributes.Class]
public class Contact : DomainClass
{
[NHibernate.Mapping.Attributes.Id(Name = "Id")]
[NHibernate.Mapping.Attributes.Generator(1, Class ="Identity")]
public virtual int ID { get; set; }
[NHibernate.Mapping.Attributes.Property]
public virtual string Name { get; set; }
[NHibernate.Mapping.Attributes.Property]
public virtual string Town { get; set; }
}
and session code as follows:
Configuration cfg = new Configuration();
cfg.Configure();
cfg.AddInputStream(NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize(
typeof(Contact).Assembly), "DomainModel.hbm.xml");
_sessionFactory=cfg.BuildSessionFactory();
My hibernate.cfg.xml file is:
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</
property>
<property name="connection.connection_string">Server=SERVER
\EXPRESS2008;Initial Catalog=Contacts;Integrated Security=True</property>
<property name="proxyfactory.factory_class">NHibernate.ByteCode.LinFu.ProxyFactoryFac tory, NHibernate.ByteCode.LinFu</property>
</session-factory>
</hibernate-configuration>
Stuart,
As I understand it,"DomainModel.hbm.xml" is the file NHibernate.Mappings.Attributes should create - the exception happens before the file is created (it's not in the output directory) and so unfortunately I can't post it.
Stuart,
Thanks again for your response.
Managed to get it to work using:
using (MemoryStream stream = new MemoryStream())
{
HbmSerializer.Default.HbmNamespace = "NSpace.DomainLayer.Entities";
HbmSerializer.Default.HbmAssembly = "NSpace";
HbmSerializer.Default.Serialize(stream,
System.Reflection.Assembly.GetExecutingAssembly());
stream.Position = 0;
Configuration cfg = new Configuration();
cfg.Configure();
cfg.AddInputStream(stream);
_sessionFactory = cfg.BuildSessionFactory();
}
and specifying table names in class attributes (my oversight as these were different from class names!).
Not sure why we need to specify Namespace separately, as I assumed NHibernate could work out the types to serialize from the assembly.
Hope above helps anyone experiencing similar issue, although my impression is few people are using NHibernate.Mappings.Attributes. The documentation seems to be seriously lacking.
Whenever you use hbm.xml file you will set your configuration class like this:
Configuration cfg = new Configuration();
cfg.Configure();
// Add class mappings to configuration object
cfg.AddAssembly(Assembly.GetCallingAssembly());
ISessionFactory sessionFactory = cfg.BuildSessionFactory();
Whenever you use Nhibernate.Mapping.Attributes like classe you will use:
For example you have use Mapping.attributes in Product Class
Configuration cfg = new Configuration();
cfg.Configure();
// Add class mappings attributes to configuration object
cfg.AddInputStream(HbmSerializer.Default.Serialize(typeof(Model.Product);
ISessionFactory sessionFactory = cfg.BuildSessionFactory();
For a while, NHMA didn't auto detect classes names because they are now optional in NHibernate (in some scenarios, you would use entity-name instead).
The most recent version however restores the auto-detect behavior with a setting to turn that off if needed.
The attributes must be manually ordered because .NET doesn't guarantee it when compiling (unlike Java).
The end goal of NHMA is to faithfully duplicate the way you write the XML version; so if you have:
<something>
<innerData/>
</something>
The NHMA version will be:
[Something]
[InnerData(2)]
NHMA tries to be smart about deducing certain values (like names), but it will only do so if the value is required.
It also provide helpers like NameType=typeof(XXX) to benefit from intellisense, compile time verification and refactoring.
More details in the documentation:
http://www.nhforge.org/doc/nh/en/index.html#mapping-attributes
If you have not solved your problem yet, try this
[NHibernate.Mapping.Attributes.Class(Table="youtable",NameType=typeof(Contact ))]
public class Contact : DomainClass
{
[NHibernate.Mapping.Attributes.Id(Name = "Id")]
[NHibernate.Mapping.Attributes.Generator(1, Class ="Identity")]
public virtual int ID { get; set; }
[NHibernate.Mapping.Attributes.Property(Name="Name")]
public virtual string Name { get; set; }
[NHibernate.Mapping.Attributes.Property(Name="Town")]
public virtual string Town { get; set; }
}
I'm using like this and it works fine .....
I must say that NHibnerate.Mapping.Atributes component was poorly written.
The code below will lead to a generation of malformed mapping xml
[Class()]
public class Bar{}
Meanwhile, the code below is fine:
[Class(Name="Bar")]
public class Bar{}
Further more, if you place [Generator] attribute after [Id] attribute, then the information about generator will not be included in the xml, but place [Generator] before [Id] will do the jobs.
The 3.0 is under working and I hope that these "nasty" bugs will be fixed.
As indicted above, you have to specify the class' Name since version 2...
I have posted an article on how to work around this by deriving the HbmWriter: http://blog.hopla.org/2009/12/fix-for-nhibernate-mapping-attributes-2-1/
Almost copy of mitjast's answer, with minor fixes and formatting:
[Class(NameType = typeof(DomainClass))]
public class DomainClass
{
[NHibernate.Mapping.Attributes.Generator(Class = "guid")]
[NHibernate.Mapping.Attributes.Id(Name = "DomainID")]
public virtual Guid DomainID { get; set; }
[NHibernate.Mapping.Attributes.Property]
public virtual string Name { get; set; }
}
That definition helped me get through all exceptions and generate valid hbm mapping.
As Mr Cold mentioned, the order of Generator and Id attributes does matter.
But in my case it was discovered that while for one class Id should go first to be actually mentioned within hbm, for another class the first attribute should be Generator. When I change the order of these attributes in one class so that both classes have equal order, the hbm for one of them becomes wrong...
The situation did not change after the release of NHibernate.Mapping.Attributes-for-NHibernate-3.0.0.Alpha2.
Presence of such quirky things looks enough for me to switch to another solution unfortunately...
Mapping a collection of enums with NHibernate
Specifically, using Attributes for the mappings.
Currently I have this working mapping the collection as type Int32 and NH seems to take care of it, but it's not exactly ideal.
The error I receive is "Unable to determine type" when trying to map the collection as of the type of the enum I am trying to map.
I found a post that said to define a class as
public class CEnumType : EnumStringType {
public CEnumType() : base(MyEnum) { }
}
and then map the enum as CEnumType, but this gives "CEnumType is not mapped" or something similar.
So has anyone got experience doing this?
So anyway, just a simple reference code snippet to give an example with
[NHibernate.Mapping.Attributes.Class(Table = "OurClass")]
public class CClass : CBaseObject
{
public enum EAction
{
do_action,
do_other_action
};
private IList<EAction> m_class_actions = new List<EAction>();
[NHibernate.Mapping.Attributes.Bag(0, Table = "ClassActions", Cascade="all", Fetch = CollectionFetchMode.Select, Lazy = false)]
[NHibernate.Mapping.Attributes.Key(1, Column = "Class_ID")]
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "Int32")]
public virtual IList<EAction> Actions
{
get { return m_class_actions; }
set { m_class_actions = value;}
}
}
So, anyone got the correct attributes for me to map this collection of enums as actual enums? It would be really nice if they were stored in the db as strings instead of ints too but it's not completely necessary.
You will need to map your CEnum type directly. In XML mappings this would mean creating a new class mapping element in your NHibernate XML mappings file.
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="YourAssembly"
auto-import="true" default-lazy="false">
...
<class name="YourAssemblyNamespace.CEnum" table="CEnumTable" mutable="false" >
<id name="Id" unsaved-value="0" column="id">
<generator class="native"/>
</id>
...
</class>
</hibernate-mapping>
To do it with attribute mappings, something like this on top of your CEnum class:
[NHibernate.Mapping.Attributes.Class(Table = "CEnumTable")] //etc as you require
This is the way i do it. There's probably an easier way but this works for me.
Edit: sorry, i overlooked that you want it as a list. I don't know how to do that...
Edit2: maybe you can map it as a protected IList[string], and convert to public IList[EAction] just as i do with a simple property.
public virtual ContractGroups Group
{
get
{
if (GroupString.IsNullOrEmpty())
return ContractGroups.Default;
return GroupString.ToEnum<ContractGroups>(); // extension method
}
set { GroupString = value.ToString(); }
}
// this is castle activerecord, you can map this property in NH mapping file as an ordinary string
[Property("`Group`", NotNull = true)]
protected virtual string GroupString
{
get;
set;
}
/// <summary>
/// Converts to an enum of type <typeparamref name="TEnum"/>.
/// </summary>
/// <typeparam name="TEnum">The type of the enum.</typeparam>
/// <param name="self">The self.</param>
/// <returns></returns>
/// <remarks>From <see href="http://www.mono-project.com/Rocks">Mono Rocks</see>.</remarks>
public static TEnum ToEnum<TEnum>(this string self)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
Argument.SelfNotNull(self);
return (TEnum)Enum.Parse(typeof(TEnum), self);
}
instead of
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "Int32")]
try
[NHibernate.Mapping.Attributes.Element(2, Column = "EAction", Type = "String")]
ie: change the Int32 to String
While I haven't tried using it myself, I stumbled across this code a little while ago and it looks pretty interesting:
http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/08/12/enumeration-classes.aspx
Like I said, I haven't used it myself, but I'm going to give it a go in a project RSN.