I have an xsd-generated class from a rich-text field as follows:
public partial class Rejection_Reason {
/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
[System.Xml.Serialization.XmlAnyElementAttribute()]
public System.Xml.XmlNode[] Any;
}
Note that the class is generated as an array of XMLNode (not sure why?). The value is set to null and anytime I attempt to set the innertext of Any[0], a nullreference exception occurs. How can I set the value of my Rejection Reason class?
I have tried:
Rejection_Reason rej = new Rejection_Reason();
XmlNode[] en = rej.Any;
en[0].InnerText = "";
But this returns null reference exception when accessing en[0].InnerText
Related
I have the following model:
Public Class MyModel
Public Property MyModelId As Integer
Public Property Description As String
Public Property AnotherProperty As String
End Class
Is there a method to get a property name of the Model as a string representation like the following code?
Dim propertyName as String = GetPropertyNameAsStringMethod(MyModel.Description)
So the propertyName variable has "Description" as value.
Check the Darin Dimitrov' answer on this SO thread - Reflection - get property name.
class Foo
{
public string Bar { get; set; }
}
class Program
{
static void Main()
{
var result = Get<Foo, string>(x => x.Bar);
Console.WriteLine(result);
}
static string Get<T, TResult>(Expression<Func<T, TResult>> expression)
{
var me = expression.Body as MemberExpression;
if (me != null)
{
return me.Member.Name;
}
return null;
}
}
Hope this help..
Here is a helper extension method you can use for any property:
public static class ReflectionExtensions
{
public static string PropertyName<T>(this T owner,
Expression<Func<T, object>> expression) where T : class
{
if (owner == null) throw new ArgumentNullException("owner");
var memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member.Name;
}
}
However, this will only work on instances of a class. You can write a similar extension method that will operate directly on the type instead.
You need to do it using reflection.
There are already loads of posts on stack overflow like this:
How to get current property name via reflection?
Reflection - get property name
Get string name of property using reflection
Reflection - get property name
I believe that the answer will be along the lines of:
string prop = "name";
PropertyInfo pi = myObject.GetType().GetProperty(prop);
Create an extension method and then use it where needed.
Private Shared Function GetPropertyName(Of T)(exp As Expression(Of Func(Of T))) As String
Return (DirectCast(exp.Body, MemberExpression).Member).Name
End Function
have a look at this post as well.
I have solved this issue editing a bit #NiranjanKala's source example,
converting the code in vb.Net like this
<System.Runtime.CompilerServices.Extension()> _
Public Function GetPropertyName(Of T, TResult)(expression As Expression(Of Func(Of T, TResult))) As String
Dim [me] = TryCast(expression.Body, MemberExpression)
If [me] IsNot Nothing Then
Return [me].Member.Name
End If
Return Nothing
End Function
Then I am able to call the extension like this
Dim propertyName as String = GetPropertyName(Of MyModel, String)(Function(x) x.Description)
Then propertyName variable has "Description" as string value.
Using com.sun.xml.bind jaxb-impl 2.2.6.
I've got parent and child object, what I would like to get after marshall is:
<parent/> or <parent></parent>
But what I get is an error like:
Caused by:
com.sun.xml.internal.bind.api.AccessorException: Object must have some value in its #XmlValue field: com.test.data.Child#....
Here is the code:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {"content"})
#XmlRootElement(name = "child")
public class Child {
#XmlValue
protected String content;
public String getContent() {
return content;
}
}
...
Then
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {"child"})
#XmlRootElement(name = "parent")
public class Parent {
protected Child child;
public Child getChild() {
return child;
}
}
What I do is (that's because I want to be able to "maybe" fill the content of child later):
if(parent.getChild == null)
parent.setChild(new Child());
Then, when I try to marshall, I get the error seen above.
If I try:
if(parent.getChild == null) {
Child child = new Child();
child.setContent("");
parent.setChild(child);
}
Then I get the expected:
<parent><child></child></parent>
And I can get what I want by never setting the child object in its parent (so no parent.setChild(...)), but as stated above, it would save me time to set it earlier.
Basically I am trying to do exactly the opposite as in the post:
JAXB: Empty string does not produce empty element
Is there anything I am doing wrong, or maybe something I did not setup correctly?
Thanks,
Yoann
Edit:
Is there any other way than to either:
- Set it back to null later: parent.setChild(null);
- Never set it as an object in the first place
Using Entity Framework I can create concrete classes from most of the sprocs in the database of a project I'm working on. However, some of the sprocs use dynamic SQL and as such no metadata is returned for the sproc.
So for a that sproc, I manually created a concrete class and now want to map the sproc output to this class and return a list of this type.
Using the following method I can get a collection of objects:
var results = connection.Query<object>("get_buddies",
new { RecsPerPage = 100,
RecCount = 0,
PageNumber = 0,
OrderBy = "LastestLogin",
ProfileID = profileID,
ASC = 1},
commandType: CommandType.StoredProcedure);
My concrete class contains
[DataContractAttribute(IsReference=true)]
[Serializable()]
public partial class LoggedInMember : ComplexObject
{
/// <summary>
/// No Metadata Documentation available.
/// </summary>
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.Int16 RowID
{
get
{
return _RowID;
}
set
{
OnRowIDChanging(value);
ReportPropertyChanging("RowID");
_RowID = StructuralObject.SetValidValue(value);
ReportPropertyChanged("RowID");
OnRowIDChanged();
}
}
private global::System.Int16 _RowID;
partial void OnRowIDChanging(global::System.Int16 value);
partial void OnRowIDChanged();
[EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String NickName
{
get
{
return _NickName;
}
set
{
OnNickNameChanging(value);
ReportPropertyChanging("NickName");
_NickName = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("NickName");
OnNickNameChanged();
}
}
private global::System.String _NickName;
partial void OnNickNameChanging(global::System.String value);
partial void OnNickNameChanged();
.
.
.
Without having to iterate through the results and add the output parameters to the LoggedInMember object, how do I map these on the fly so I can return a list of them through a WCF service?
If I try var results = connection.Query<LoggedInMember>("sq_mobile_get_buddies_v35", ... I get the following error:
System.Data.DataException: Error parsing column 0 (RowID=1 - Int64)
---> System.InvalidCastException: Specified cast is not valid. at Deserialize...
At a guess your SQL column is a bigint (i.e. Int64 a.k.a. long) but your .Net type has a n Int16 property.
You could play around with the conversion and ignore the stored procedure by doing something like:
var results = connection.Query<LoggedInMember>("select cast(9 as smallint) [RowID] ...");
Where you are just selecting the properties and types you want to return your object. (smallint is the SQL equivalent of Int16)
The solution to this was to create a complex object derived from the sproc with EF:
public ProfileDetailsByID_Result GetAllProfileDetailsByID(int profileID)
{
using (IDbConnection connection = OpenConnection("PrimaryDBConnectionString"))
{
try
{
var profile = connection.Query<ProfileDetailsByID_Result>("sproc_profile_get_by_id",
new { profileid = profileID },
commandType: CommandType.StoredProcedure).FirstOrDefault();
return profile;
}
catch (Exception ex)
{
ErrorLogging.Instance.Fatal(ex); // use singleton for logging
return null;
}
}
}
In this case, ProfileDetailsByID_Result is the object that I manually created using Entity Framework through the Complex Type creation process (right-click on the model diagram, select Add/Complex Type..., or use the Complex Types tree on the RHS).
A WORD OF CAUTION
Because this object's properties are derived from the sproc, EF has no way of knowing if a property is nullable. For any nullable property types, you must manually configure these by selecting the property and setting its it's Nullable property to true.
I am having some issues in the mapping mentioned in the title. Here are the details:
class MyDomain
{
public Iesi.Collections.Generic.ISet<SomeType> MySomeTypes{ get; set; }
....
}
class MyDTO
{
public IList<SomeTypeDTO> MySomeTypes{ get; set; }
...
}
The mapping:
Mapper.CreateMap<MyDomain, MyDTO>().ForMember(dto=>dto.MySomeTypes, opt.ResolveUsing<DomaintoDTOMySomeTypesResolver>());
Mapper.CreateMap<MyDTO, MyDomain>().ForMember(domain=>domain.MySomeTypes, opt.ResolveUsing<DTOtoDomainMySomeTypesResolver>());
The Resolvers:
class DomaintoDTOMySomeTypesResolver: ValueResolver<MyDomain, IList<SomeTypeDTO>>
{
protected override IList<SomeTypeDTO> ResolveCore(MyDomain source)
{
IList<SomeTypeDTO> abc = new List<DemandClassConfigurationDTO>();
//Do custom mapping
return abc;
}
}
class DTOtoDomainMySomeTypesResolver: ValueResolver<MyDTO, Iesi.Collections.Generic.ISet<SomeType>>
{
protected override Iesi.Collections.Generic.ISet<SomeType> ResolveCore(SystemParameterDTO source)
{
Iesi.Collections.Generic.ISet<SomeType> abc = new HashedSet<SomeType>();
//Do custom mapping
return abc;
}
}
Mapping from Domain to DTO works ok and as expected I get a MyDTO object with IList of "SomeTypeDTO" objects.
However mapping of the DTO to Domain throws the following error:
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> AutoMapper.AutoMapperMappingException : Trying to map Iesi.Collections.Generic.HashedSet`1[SomeType, MyAssembly...] to Iesi.Collections.Generic.ISet`1[SomeType, MyAssembly...]
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> System.InvalidCastException : Unable to cast object of type 'System.Collections.Generic.List`1[SomeType]' to type 'Iesi.Collections.Generic.ISet`1[SomeType]
What might I be doing wrong and what do the error messages imply? It almost seems that automapper is having some issues in mapping the ISet ( together with its concrete implementation HashedSet). My understanding is that in the above described scenario automapper should just use the ISet reference returned by "DTOtoDomainMySomeTypesResolver". I also don't see why I am getting the "cast from List to ISet error".
This is because AutoMapper currently doesn't support ISet<> collection properties. It works when the destination property of ISet<> is already instantiated (is not null), because the ISet<> actually inherits from ICollection<>, thus Automapper can understand that and will do the collection mapping properly.
It doesn't work when the destination property is null and is interface type. You get this error, because automapper actually found out it can be assigned from ICollection<> so it instantiates the property using generic List<>, which is default collection when automapper must create new collection property, but then when it tries to actually assign it, it will fail, because obviously List<> cannot be cast to ISet<>
There are three solution to this:
Create a feature request to support ISet<> collections and hope they will add it
Make sure the property is not null. Eg. instantiate it in constructor to empty HashSet<>. This might cause some troubles for ORM layers, but is doable
The best solution that I went with is to create custom value resolver, which you already have and instantiate the property yourself if it is null. You need to implement the IValueResolver, because the provided base ValueResolver will not let you instantiate the property. Here is the code snippet that I used:
public class EntityCollectionMerge : IValueResolver
where TDest : IEntityWithId
where TSource : IDtoWithId
{
public ResolutionResult Resolve(ResolutionResult source)
{
//if source collection is not enumerable return
var sourceCollection = source.Value as IEnumerable;
if (sourceCollection == null) return source.New(null, typeof(IEnumerable));
//if the destination collection is ISet
if (typeof(ISet).IsAssignableFrom(source.Context.DestinationType))
{
//get the destination ISet
var destSet = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ISet;
//if destination set is null, instantiate it
if (destSet == null)
{
destSet = new HashSet();
source.Context.PropertyMap.DestinationProperty.SetValue(source.Context.DestinationValue, destSet);
}
Merge(sourceCollection, destSet);
return source.New(destSet);
}
if (typeof(ICollection).IsAssignableFrom(source.Context.DestinationType))
{
//get the destination collection
var destCollection = source.Context.PropertyMap.GetDestinationValue(source.Context.DestinationValue) as ICollection;
//if destination collection is null, instantiate it
if (destCollection == null)
{
destCollection = new List();
source.Context.PropertyMap.DestinationProperty.SetValue(source.Context.DestinationValue, destCollection);
}
Merge(sourceCollection, destCollection);
return source.New(destCollection);
}
throw new ArgumentException("Only ISet and ICollection are supported at the moment.");
}
public static void Merge(IEnumerable source, ICollection destination)
{
if (source == null) return;
var destinationIds = destination.Select(x => x.Id).ToHashSet();
var sourceDtos = source.ToDictionary(x => x.Id);
//add new or update
foreach (var sourceDto in sourceDtos)
{
//if the source doesnt exist in destionation add it
if (sourceDto.Key (sourceDto.Value));
continue;
}
//update exisiting one
Mapper.Map(sourceDto.Value, destination.First(x => x.Id == sourceDto.Key));
}
//delete entity in destination which were removed from source dto
foreach (var entityToDelete in destination.Where(entity => !sourceDtos.ContainsKey(entity.Id)).ToList())
{
destination.Remove(entityToDelete);
}
}
}
Then on your mapping use opt => opt.ResolveUsing(new EntitCollectionMerge<Entity,Dto>()).FromMember(x => x.ISetMember) or if you have lots of collection like this you can add them automatically to all of them via typeMaps.
I have the following java class and have placed an XmlJavaAdapter annotation on the payerPartyReference variable. I want the adapter PartyReferenceAdapter to be used for unmarshalling ONLY this variable, not any other variables which have the same type of PartyReference, whether in this class or some other class. How can I do this? Thanks for your help!
public class InitialPayment extends PaymentBase
{
// Want PartyReferenceAdapter to be used here
#XmlJavaTypeAdapter(PartyReferenceAdapter.class)
protected PartyReference payerPartyReference;
//
// Dont want PartyReferenceAdapter to be used here
protected PartyReference receiverPartyReference;
//
protected AccountReference receiverAccountReference;
#XmlSchemaType(name = "date")
protected XMLGregorianCalendar adjustablePaymentDate;
#XmlSchemaType(name = "date")
protected XMLGregorianCalendar adjustedPaymentDate;
protected Money paymentAmount;
}
My Adapter is defined as follows:
public class PartyReferenceAdapter
extends XmlAdapter < Object, PartyReference > {
public PartyReference unmarshal(Object obj) throws Exception {
Element element = null;
if (obj instanceof Element) {
element = (Element)obj;
String reference_id = element.getAttribute("href");
PartyReference pr = new PartyReference();
pr.setHref(reference_id);
return pr;
}
public Object marshal(PartyReference arg0) throws Exception {
return null;
}
}
Field/Property Level
If you set #XmlJavaTypeAdapter on a field/property it will only be used for that property.
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
Type Level
If you set #XmlJavaTypeAdapter on a type, then it will used for all references to that type.
http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
Package Level
If you set #XmlJavaTypeAdapter on a package, then it will be used for all references to that type within that package:
http://bdoughan.blogspot.com/2011/05/jaxb-and-joda-time-dates-and-times.html