Deserialization of XDocument doesn't work - c#-4.0

I have this code:
[Serializable]
[XmlRoot("ISO_CCY_CODES")]
public class IsoCurrencyCodes
{
public IsoCurrencyCodes()
{
IsoCodes = new List<IsoCurrencyCode>();
}
public IsoCurrencyCodes(List<IsoCurrencyCode> isoCodes)
{
IsoCodes = isoCodes;
}
[XmlArrayItem("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
public static IEnumerable<IsoCurrencyCode> Get()
{
var doc = XDocument.Parse(XmlStringIsoCodes.XmlSource.Replace("\r\n", ""));
var res = doc.Deserialize<IsoCurrencyCodes>();
return res.IsoCodes;
}
}
[Serializable]
[XmlRoot("ISO_CURRENCY")]
public class IsoCurrencyCode
{
public IsoCurrencyCode()
{
}
[XmlElement(ElementName = "ENTITY")]
public string Entity { get; set; }
[XmlElement(ElementName = "CURRENCY")]
public string Currency { get; set; }
[XmlElement(ElementName = "ALPHABETIC_CODE")]
public string Alpha_Code3 { get; set; }
[XmlElement(ElementName = "NUMERIC_CODE")]
public int NumCode { get; set; }
[XmlElement(ElementName = "MINOR_UNIT")]
public string MinorUnit { get; set; }
}
And this code, for deserialization:
public static XDocument Serialize<T>(this T source)
{
var ser = new XmlSerializer(source.GetType());
var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
ser.Serialize(writer, source);
}
return XDocument.Parse(sb.ToString());
}
public static T Deserialize<T>(this XDocument xmlDocument)
{
var xmlSerializer = new XmlSerializer(typeof (T));
using (var reader = xmlDocument.CreateReader())
return (T) xmlSerializer.Deserialize(reader);
}
This is the XML source
But deserialization doesn't work. Please help.
Thanks!

I believe you only want to use XMLArray if you have a collection element for all of the items to sit underneath. For example here it could be ISO_CURRENCIES. I'm assuming you can't change the source in this case, so just use this instead:
[XmlElement("ISO_CURRENCY")]
public List<IsoCurrencyCode> IsoCodes { get; set; }
You should find that works.
Additionally, if you find you have further problems in getting the deserialization classes right, you can have them autogenerated for you from the XML and then you can take a look at the code that is created:
xsd source.xml
xsd source.xsd /c
This will create source.cs which you can then use in your project or adapt for your own uses.
As a further note, you'll find you can't use int for Minor_Unit as it's nullable (look at ANTARCTICA). You can't deserialize straight to an int?, so you'll either have to make it a string or go via another property, look at this question for more information.

Related

How to get the current cache/document (Sales Order/Shipment) outside the context of a graph

I'm currently implementing a new carrier method and would like to access additional information on the Shipment/Sales Order object which is not passed through in the GetRateQuote & Ship functions of the implemented ICarrierService class.
The carrier method implements the ICarrierService interface and subsequently does not have access to a Graph where one would typically be able to access the current (cached?) document, etc.
How could I, for example, access the shipment number for which the Ship function is called?
My ultimate goal is to be able to generate a label for the shipment package, and in order to do so, I need to obtain the Shipment Number.
using PX.Api;
using PX.CarrierService;
using PX.CS.Contracts.Interfaces;
using PX.Data;
using PX.Data.Reports;
using PX.Objects.Common.Extensions;
using PX.Reports;
using PX.Reports.Data;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyCarriers.CollectCarrier
{
public class CollectCarrier : ICarrierService
{
private List<CarrierMethod> methods;
private List<string> attributes;
public IList<string> Attributes => (IList<string>)this.attributes;
public string CarrierID { get; set; }
public string Method { get; set; }
public ReadOnlyCollection<CarrierMethod> AvailableMethods => this.methods.AsReadOnly();
public CollectCarrier()
{
this.methods = new List<CarrierMethod>(1);
this.methods.Add(new CarrierMethod("CLT", "Collect"));
this.attributes = new List<string>(1);
}
[...]
public CarrierResult<ShipResult> Ship(CarrierRequest cr)
{
if (cr.Packages == null || cr.Packages.Count == 0)
{
throw new InvalidOperationException("CarrierRequest.Packages must be contain atleast one Package");
}
CarrierResult<ShipResult> carrierResult;
try
{
CarrierResult<RateQuote> rateQuote = this.GetRateQuote(cr, true);
ShipResult result = new ShipResult(rateQuote.Result);
//Report Parameters
Dictionary<String, String> parameters = new Dictionary<String, String>();
// ************************************************************************************
// At this point, I would like to be able to retrieve the current SOShipment's Shipment Number
// ************************************************************************************
parameters["shipmentNbr"] = "000009"; // Hard-coded this value to get the PDF generated.
//Report Processing
PX.Reports.Controls.Report _report = PXReportTools.LoadReport("SO645000", null);
PXReportTools.InitReportParameters(_report, parameters, SettingsProvider.Instance.Default);
ReportNode reportNode = ReportProcessor.ProcessReport(_report);
//Generation PDF
result.Image = PX.Reports.Mail.Message.GenerateReport(reportNode, ReportProcessor.FilterPdf).First();
result.Format = "pdf";
result.Data.Add(new PackageData(
cr.Packages.FirstOrDefault().RefNbr,
this.RandomString(6),
result.Image,
"pdf"
)
{
TrackingData = this.RandomString(6)
});
carrierResult = new CarrierResult<ShipResult>(result);
}
catch (Exception ex)
{
if (this.LogTrace)
{
this.WriteToLog(ex, this.GetType().Name + ".Ship().Exception");
}
List<Message> messageList = this.HandleException(ex);
messageList?.Insert(0, new Message("", "Failed to generate the collection label: "));
carrierResult = new CarrierResult<ShipResult>(false, null, (IList<Message>)messageList);
}
return carrierResult;
}
[...]
}
}
For reference, the CarrierRequest object that is passed to the functions contain the following information:
public class CarrierRequest
{
public string ThirdPartyAccountID
{
get;
set;
}
public string ThirdPartyPostalCode
{
get;
set;
}
public string ThirdPartyCountryCode
{
get;
set;
}
public IAddressBase Shipper
{
get;
set;
}
public IContactBase ShipperContact
{
get;
set;
}
public IAddressBase Origin
{
get;
set;
}
public IContactBase OriginContact
{
get;
set;
}
public IAddressBase Destination
{
get;
set;
}
public IContactBase DestinationContact
{
get;
set;
}
public IList<CarrierBox> Packages
{
get;
set;
}
public IList<CarrierBoxEx> PackagesEx
{
get;
set;
}
public IList<string> Methods
{
get;
set;
}
public DateTime ShipDate
{
get;
set;
}
public UnitsType Units
{
get;
private set;
}
public bool SaturdayDelivery
{
get;
set;
}
public bool Resedential
{
get;
set;
}
public bool Insurance
{
get;
set;
}
public string CuryID
{
get;
private set;
}
public IList<string> Attributes
{
get;
set;
}
public decimal InvoiceLineTotal
{
get;
set;
}
public string FreightClass
{
get;
set;
}
public bool SkipAddressVerification
{
get;
set;
}
public IList<ISETerritoriesMappingBase> TerritoriesMapping
{
get;
set;
}
public CarrierRequest(UnitsType units, string curyID)
{
if (string.IsNullOrEmpty(curyID))
{
throw new ArgumentNullException("curyID");
}
Units = units;
CuryID = curyID;
}
}
I have seen a similar question here on SO, but I'm not entirely sure that is applicable to my specific request?
Any assistance will be highly appreciated.
See below as an option to loop through your currents and search for the specific current object:
SOShipment ship = null;
for (int i = 0; i < Caches.Currents.Length; i++)
{
if (Caches.Currents[i].GetType() == typeof(SOShipment))
{
ship = (SOShipment)Caches.Currents[i];
break;
}
}

Create field in SharePoint programmatically using CSOM (Not with XML)

Is it possible to create fields in SharePoint with CSOM, not using XML?
I've seen many examples using XML, but none with just setting properties for the field programmatically?
fields.Add(new **FieldCreationInformation** {
InternalName = "Test",
etc..
});
That's doable, in the following example is introduced a FieldCreationInformation class:
[XmlRoot("Field")]
public class FieldCreationInformation
{
[XmlAttribute("ID")]
public Guid Id { get; set; }
[XmlAttribute()]
public string DisplayName { get; set; }
[XmlAttribute("Name")]
public string InternalName { get; set; }
[XmlIgnore()]
public bool AddToDefaultView { get; set; }
//public IEnumerable<KeyValuePair<string, string>> AdditionalAttributes { get; set; }
[XmlAttribute("Type")]
public FieldType FieldType { get; set; }
[XmlAttribute()]
public string Group { get; set; }
[XmlAttribute()]
public bool Required { get; set; }
public string ToXml()
{
var serializer = new XmlSerializer(GetType());
var settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, this, emptyNamepsaces);
return stream.ToString();
}
}
public FieldCreationInformation()
{
Id = Guid.NewGuid();
}
}
and then extension method for creating a new field:
public static class FieldCollectionExtensions
{
public static Field Add(this FieldCollection fields, FieldCreationInformation info)
{
var fieldSchema = info.ToXml();
return fields.AddFieldAsXml(fieldSchema, info.AddToDefaultView, AddFieldOptions.AddFieldToDefaultView);
}
}
Usage
var fieldInfo = new FieldCreationInformation();
fieldInfo.FieldType = FieldType.Geolocation;
fieldInfo.InternalName = "ContactsLocation";
fieldInfo.DisplayName = "Contacts Location";
ctx.Site.RootWeb.Fields.Add(fieldInfo);
ctx.ExecuteQuery();
When I add fields with CSOM/JSOM I use the method on the FieldCollection AddFieldAsXml. This requires you to build a string of xml with all of the properties for the desired field, but it works. I included an excerpt of the related cpde below:
Microsoft.SharePoint.Client.Web web = _context.Web;
FieldCollection fields = web.Fields;
_context.Load(fields);
_context.ExecuteQuery();
Field field = fields.FirstOrDefault(f => f.StaticName == _staticName);
if (field == null)
{
Field createdField = fields.AddFieldAsXml(xml, false, AddFieldOptions.AddToNoContentType);
_context.Load(createdField);
_context.ExecuteQuery();
}
Similar code is used if you would like to add a field directly to an existing list.

Generic Automapper function with custom convension for underscored properties

I simply need to map some auto generated classes from database to domain/viewmodels classes. The autogenerated class may have names like customer_id that I want to be mapped with CustomerId. Somehow I want to register my own convention with auto mapper. I have started with following code however the mapped object properties are not populated:
// Generic method that should map source to target
public static TTarget MapWithUnderScoreConvension(TSource source, TTarget target)
{
Mapper.Initialize(cfg=> cfg.AddProfile<AutoMapperUnderScoreProfile>());
Mapper.CreateMap<TSource, TTarget>();
var mapped = Mapper.Map(source, target);
return mapped;
}
// New underscore profile
public class AutoMapperUnderScoreProfile : Profile
{
public AutoMapperUnderScoreProfile()
{
Mapper.Initialize(configuration => configuration.CreateProfile("UnderScoreProfile", UnderScoreProfile));
Mapper.AssertConfigurationIsValid();
}
private void UnderScoreProfile(IProfileExpression profile)
{
profile.SourceMemberNamingConvention = new PascalCaseNamingConvention();
profile.DestinationMemberNamingConvention = new SourceUnderScoreNamingConvension();
}
}
// Convention class
public class SourceUnderScoreNamingConvension : INamingConvention
{
private readonly string _separatorCharacter="_";
private readonly Regex _splittingExpression = new Regex(#"[\p{Lu}0-9]+(?=_?)");
public Regex SplittingExpression { get { return _splittingExpression;} private set{} }
public string SeparatorCharacter { get { return _separatorCharacter; } private set{} }
}
// Test cases
[TestMethod()]
public void Test_Map_Db_Generated_Class_To_Model()
{
var dbGenerated = new QuestionTypeFromDb()
{
QuestionType_Description = "1",
QuestionType_Id = 1,
QuestionType_Is_Default = true,
QuestionType_Is_TimeBased = true,
QuestionType_Sequence = 1,
QuestionType_Time_In_Seconds = 1
};
var mappedObject = AutoMapperHelper<QuestionTypeFromDb, QuestionType>
.MapWithUnderScoreConvension(dbGenerated, new QuestionType());
mappedObject.QuestionTypeId.Should().Be(dbGenerated.QuestionType_Id);
mappedObject.QuestionTypeDescription.Should().Be(dbGenerated.QuestionType_Description);
mappedObject.TimeInSeconds.Should().Be(dbGenerated.QuestionType_Time_In_Seconds);
mappedObject.QuestionTypeSequence.Should().Be(dbGenerated.QuestionType_Sequence);
}
public class TestQuestionWithAnswerType
{
public int QuestionTypeId { get; set; }
public string QuestionTypeDescription { get; set; }
public int QuestionTypeSequence { get; set; }
public bool QuestionTypeIsTimeBased { get; set; }
public int? QuestionTypeTimeInSeconds { get; set; }
public bool QuestionTypeIsDefault { get; set; }
}
any comments will be appreciated.
Update
I have found that the following workaround works:
I simply replaced used this -> to replace 'underscore' with nothing (Mapper.Initialize(c => c.ReplaceMemberName("_", ""));
public static TTarget MapWithUnderScoreConvension(TSource source, TTarget target)
{
Mapper.Initialize(c => c.ReplaceMemberName("_", ""));
//Mapper.Initialize(cfg => cfg.AddProfile<AutoMapperUnderScoreProfile>());
Mapper.CreateMap<TSource, TTarget>();
var mapped = Mapper.Map(source, target);
return mapped;
}
Your regex needs to be changed to : [\p{L}}0-9]+(?=_?)
This will take care of Customer_Id, CUSTOMER_ID, customer_id
The {L} unicode category includes Lu, Lt, Ll, Lm and Lo characters as mentioned here.
Answer is added in the Update section of the question. Basically the solution for me was very simple -> Mapper.Initialize(c => c.ReplaceMemberName("_", ""));

Xml Parsing in C#

http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom on this site, I wrote the following code to retrieve the data.
protected void Button1_Click(object sender, EventArgs e)
{
string adresal = "http://" + txtAd.Text;
WebResponse GelenCevap;
WebRequest adresistegi = HttpWebRequest.Create(adresal);
GelenCevap = adresistegi.GetResponse();
StreamReader CevapOku = new StreamReader(GelenCevap.GetResponseStream());
string KaynakKodlar = CevapOku.ReadToEnd();
XmlDocument xmlDoc = new XmlDocument(); // Create an XML document object
xmlDoc.Load("http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.atom"); // Load the XML document from the specified file
XmlNodeList depremler = xmlDoc.GetElementsByTagName("entry");
foreach (XmlNode node in depremler)
{
var a = node.ChildNodes;
foreach (XmlElement x in a)
{
ListBox1.Items.Add(x.InnerText);
}
}
}
In this way I get all the data in the ListBox.But I need to assign them to variable data line by line.How can I do? I would appreciate if you can help.
Also i need id,title, updated, georss:point,georss:elev variables.
First add an Enrty and Category class:
public class Entry
{
public string Id { get; set; }
public string Title { get; set; }
public string Updated { get; set; }
public string Summary { get; set; }
public string GPoint { get; set; }
public string GElev { get; set; }
public List<string> Categories { get; set; }
}
public class Category
{
public string Label { get; set; }
public string Term { get; set; }
}
Then use LINQ to XML
XDocument xDoc = XDocument.Load("path");
List<Entry> entries = (from x in xDoc.Descendants("entry")
select new Entry()
{
Id = (string) x.Element("id"),
Title = (string)x.Element("title"),
Updated = (string)x.Element("updated"),
Summary = (string)x.Element("summary"),
GPoint = (string)x.Element("georss:point"),
GElev = (string)x.Element("georss:elev"),
Categories = (from c in x.Elements("category")
select new Category
{
Label = (string)c.Attribute("label"),
Term = (string)c.Attribute("term")
}).ToList();
}).ToList();

Automapper map custom collections

Hello. I have a list that looks like this one:
public class PagedList<T> : List<T>
{
public PagedList(IEnumerable<T> collection) : base(collection)
{ }
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
//some other properties
}
and used in repository for paging
public PagedList<TEntity> GetPaged(int page)
{
var pagedEntities = some_query;
return pagedEntities.AsPagedList(totalResults, page, pageSize);
}
The same PagedList is also used in asp mvc view models for paging.
Is it possible to map this collections using Automapper with all the properties TotalItems/CurrentPage/... ?
PagedList<DbItem> dbItems = _repository.GetPages(page);
var viewItems = new PagedList<SomeItemView>();
Mapper.Map(dbItems , viewItems);
Tahnk You !
This worked for me. Are you looking for something more generic?
public class DbItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ViewItem
{
public int Id { get; set; }
public string Name { get; set; }
}
public class PagedList<T>: List<T>
{
public int TotalItems { get; set; }
public int CurrentPage { get; set; }
public int PageSize { get; set; }
}
class Program
{
static void Main(string[] args)
{
MapItems();
}
public static void MapItems()
{
Mapper.CreateMap<DbItem, ViewItem>();
Mapper.CreateMap<PagedList<DbItem>, PagedList<ViewItem>>()
.AfterMap((s, d) => Mapper.Map<List<DbItem>, List<ViewItem>>(s, d));
var dbList = new PagedList<DbItem>
{
new DbItem {Id = 1, Name = "a"},
new DbItem {Id = 2, Name = "b"}
};
dbList.TotalItems = 2;
dbList.CurrentPage = 1;
dbList.PageSize = 10;
var viewList = Mapper.Map<PagedList<DbItem>, PagedList<ViewItem>>(dbList);
Console.WriteLine(viewList.TotalItems);
Console.WriteLine(viewList.CurrentPage);
Console.WriteLine(viewList.PageSize);
Console.WriteLine(viewList[0].Id + " " + viewList[0].Name);
Console.WriteLine(viewList[1].Id + " " + viewList[1].Name);
Console.ReadLine();
}
}
What you need is a custom type converter
public class PagedListConverter<TIn, TOut> : ITypeConverter<IPagedList<TIn>, IPagedList<TOut>>
{
public IPagedList<TOut> Convert(AutoMapper.ResolutionContext context)
{
var source = (IPagedList<TIn>)context.SourceValue;
var mapped = Mapper.Map<IList<TOut>>(source);
return new StaticPagedList<TOut>(mapped, source.GetMetaData());
}
}
Usage:
Mapper.CreateMap<IPagedList<Company>, IPagedList<CompanyViewModel>>().ConvertUsing<PagedListConverter<Company, CompanyViewModel>>();
For those who have faced the similar problem recently, and as an update to NoPyGod's answer, you can achieve the general mapping using ITypeConverter. According to the official documentation:
AutoMapper also supports open generic type converters with any number of generic arguments:
var configuration = new MapperConfiguration(cfg =>
cfg.CreateMap(typeof(Source<>), typeof(Destination<>)).ConvertUsing(typeof(Converter<,>)));
The closed type from Source will be the first generic argument, and the closed type of Destination will be the second argument to close Converter<,>.
So the custom type converter would be:
private class Converter<TSource, TDestination>
: ITypeConverter<PagedList<TSource>, PagedList<TDestination>>
{
public PagedList<TDestination> Convert(
PagedList<TSource> source,
PagedList<TDestination> destination,
ResolutionContext context) =>
new PagedList<TDestination>(
context.Mapper.Map<List<TSource>, List<TDestination>>(source));
/* Additional settings comes here. */
}
And then register it:
this.CreateMap(typeof(PagedList<>), typeof(PagedList<>)).ConvertUsing(typeof(Converter<,>));

Resources