c# deserializate xml embedded node to class property - c#

i have part of xml document
<Tender SubTenderType="BC" TenderType="Check">
<TenderTotal>
<Amount>10.00</Amount>
</TenderTotal>
</Tender>
i need convert it to class.
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
what i already wrote and this work. but i interseing can i deserialize xml to class as written above?
[Serializable]
public class Tender
{
[XmlAttribute("SubTenderType")]
public string SubTenderType { get; set; }
[XmlAttribute("TenderType")]
public string TenderType { get; set; }
[XmlElement("TenderTotal")]
public TenderTotal TenderTotal { get; set; }
}
[Serializable]
public class TenderTotal
{
[XmlElement("Amount")]
public decimal Amount { get; set; }
}

You can deserialize xml to first Type "Tender" and next use autoMapper to map your type (create new object of different type)
create map:
config.CreateMap<TenderFirst, TenderSecond>().ForMember(x => x.TenderTotal.Amount, y => y.Amount ())

Having the following class without XmlAttribute:
public class Tender
{
public string SubTenderType { get; set; }
public string TenderType { get; set; }
public decimal Amount { get; set; }
}
You can use the XmlAttributeOverrides class to override the behavior of the serializer in such a way that instead of elements it would do the attributes.
var attrSTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("SubTenderType") };
var attrTT = new XmlAttributes { XmlAttribute = new XmlAttributeAttribute("TenderType") };
var overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Tender), nameof(Tender.SubTenderType), attrSTT);
overrides.Add(typeof(Tender), nameof(Tender.TenderType), attrTT);
var xs = new XmlSerializer(typeof(Tender), overrides);
However, in this way impossible add a new item or wrap one element in another.
Therefore, you have to do custom serialization, or map one type to another, or writing a custom xml reader/writer, or perform the read/write manually (for example, using linq2xml). There are many ways...

Related

Deserialize complex Json string with Text.Json and source generator

I would like to use Text.Json and source generator to deserialize the following Json string but I can't figure out the correct syntax/view model to provide to my JsonSerializerContext class.
var json = #"[[{""OU2CJ3-UBRYG-KCPVS1"":{""cost"":""27187.08000"",""vol_exec"":""3.60000000"",""fee"":""27.18708"",""avg_price"":""7996.20000""}}],""openOrders"",{""sequence"": 59342}]";
I have tried with the following syntax withtout success:
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(OpenOrderFrame[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class OpenOrderContext : JsonSerializerContext
{ }
public partial class OpenOrder
{
public string Fee { get; set; }
public string Cost { get; set; }
public string Vol_exec { get; set; }
public string Avg_price { get; set; }
}
public partial class OpenOrderFrame
{
public List<(string, OpenOrder)> OpenOrder { get; set; }
public string channelName { get; set; }
public int Sequence { get; set; }
}
var openOrders = JsonSerializer.Deserialize(json, OpenOrderContext.Default.OpenOrderFrameArray);
But the Deserialize method throw.
How correctly write the typeof argument of JsonSerializable attribute of my OpenOrderContext class ?
The challenge with this JSON is that it contains an outer array with elements of different types:
The first array element is an array itself that contains documents. The properties of the documents contain a key (the property name) and a value (the property value). This can be deserialized to a List<Dictionary<string, OpenOrder>>.
The second array element is a string value that contains the channel name.
The third array element is a document with a sequence property.
You can write a custom converter for this. If you only need it in one place and you have control over the deserialization, you can also use the following code to deserialize it:
// Get a Utf8JsonReader for the JSON
// (this sample converts only the string, maybe there is a more efficient way in your environment to get a Utf8JsonReader instance)
var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json));
// Parse the outer array
var o = JsonElement.ParseValue(ref reader);
var frame = new OpenOrderFrame();
// First array element contains array of documents with string keys and OpenOrder values
frame.OpenOrder = JsonSerializer.Deserialize<List<Dictionary<string, OpenOrder>>>(
o[0],
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
// Second array element contains the channel name
frame.channelName = o[1].GetString();
// Third array element contains a document with a sequence property
frame.Sequence = o[2].GetProperty("sequence").GetInt32();
Please note that I have adjusted your classes like this (especially the OpenOrder property of OpenOrderFrame):
public partial class OpenOrder
{
public string Fee { get; set; }
public string Cost { get; set; }
public string Vol_exec { get; set; }
public string Avg_price { get; set; }
}
public partial class OpenOrderFrame
{
public List<Dictionary<string, OpenOrder>> OpenOrder { get; set; }
public string channelName { get; set; }
public int Sequence { get; set; }
}
In order to use a source generator, you can create a context that generates the mapping information for List<Dictionary<string, OpenOrder>>; in the above approach, this is the only place that performs a JSON to object mapping that benefits from a source generator.
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(List<Dictionary<string, OpenOrder>>), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class OpenOrderContext : JsonSerializerContext
{ }
You can use the source generator like this:
// First array element contains array of documents with string keys and OpenOrder values
frame.OpenOrder = JsonSerializer.Deserialize<List<Dictionary<string, OpenOrder>>>(
o[0],
OpenOrderContext.Default.ListDictionaryStringOpenOrder);
Another way for doing this :
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(Dictionary<string, OpenOrder>[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class OpenOrderContext : JsonSerializerContext
{ }
public partial class OpenOrder
{
public string Fee { get; set; }
public string Cost { get; set; }
public string Vol_exec { get; set; }
public string Avg_price { get; set; }
}
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(JsonElement[]), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class ObjectContext : JsonSerializerContext
{ }
var jsonElements = JsonSerializer.Deserialize(jsonString, ObjectContext.Default.JsonElementArray);
var openOrder = JsonSerializer.Deserialize(jsonElements[0], OpenOrderContext.Default.DictionaryStringOpenOrderArray);
But it leads to a little bit more memory allocation than #Markus solution. Should be nice if we could perform only one JsonSerializer.Deserialize call.

XmlSerializer - Combining multiple elements into a list which all have the same signature but different tag names

I'm still pretty fresh to C# and XML so excuse me if I use the wrong terminology.
I'm trying to serialize an XML string into a class I'm building. The problem is, one of the elements contains a "list" of elements that all have the same structure, but they each have a different tag name...like so:
<Results>
<factors>
<factor1 code="012">Some text</factor1>
<factor2 code="123">Some text</factor2>
<factor3 code="234">Some text</factor3>
<factor4 code="345">Some text</factor4>
<factor5 code="456">Some text</factor5>
</factors>
</Results>
I've built a class like this:
public class Factor
{
[XmlAttribute("code")]
public string Code { get; set; }
[XmlText]
public string Text { get; set; }
}
public class Factors
{
[XmlElement("factor1")]
public Factor Factor1 { get; set; }
[XmlElement("factor2")]
public Factor Factor2 { get; set; }
[XmlElement("factor3")]
public Factor Factor3 { get; set; }
[XmlElement("factor4")]
public Factor Factor4 { get; set; }
[XmlElement("factor5")]
public Factor Factor5 { get; set; }
}
[XmlRoot("Results")]
public class Results
{
[XmlElement("factors")]
public Factors Factors { get; set; }
}
Since in this case, Factor1-5 all have the same "shape", is there a way for me to read these into a list instead?
This isn't something I absolutely need to figure out, but I was curious to see if there was a simple solution for it.
Try this on for size. If you don't need to re-serialize the objects, this will deserialize your example XML in (note the added ElementName on Factor if you need to know what the actual element name was. Otherwise, discard it from the class and code).
public class Factor
{
public string ElementName { get; set; }
public string Code { get; set; }
public string Text { get; set; }
}
[XmlRoot("Results")]
public class Results : IXmlSerializable
{
public Factor[] Factors { get; set; }
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader) => this.Factors = XDocument
.Load(reader.ReadSubtree())
.Descendants("factors")
.Descendants()
.Select(factorElement => new Factor
{
ElementName = factorElement.Name.ToString(),
Code = (string)factorElement.Attribute("code"),
Text = factorElement.Value
})
.ToArray();
public void WriteXml(XmlWriter writer) => throw new NotSupportedException();
}

Deserializing XML to object when they are not "shaped" the same?

I have an XML snippet like so:
<coverageCd>WEL
<descriptorCd>SMIO
<descriptorCdStartDate>01/01/2015</descriptorCdStartDate>
<descriptorCdEndDate>12/31/9999</descriptorCdEndDate>
</descriptorCd>
<descriptorCd>AAE
<descriptorCdStartDate>01/01/2015</descriptorCdStartDate>
<descriptorCdEndDate>12/31/9999</descriptorCdEndDate>
</descriptorCd>
</coverageCd>
I need to automagically translate this into the following class structure:
public class XmlCoverageCode
{
public string CoverageCode { get; set; }
public IEnumerable<XmlDescriptor> Descriptors { get; set; }
}
public class XmlDescriptor
{
public string DescriptorCode { get; set; }
public string DescriptorCodeStartDate { get; set; }
public string DescriptorCodeEndDate { get; set; }
}
...so the above XML snippet would translate to this:
var coverageCd = new XmlCoverageCode
{
CoverageCode = "WEL",
Descriptors =
new List<XmlDescriptor>
{
new XmlDescriptor
{
DescriptorCode = "SMIO",
DescriptorCodeStartDate = "01/01/2015",
DescriptorCodeEndDate = "12/31/9999"
},
new XmlDescriptor
{
DescriptorCode = "AAE",
DescriptorCodeStartDate = "01/01/2015",
DescriptorCodeEndDate = "12/31/9999"
}
}
};
Naturally, I would prefer to use built-in mechanisms for doing this. I just don't know if that's even possible.
To get classes from the XML, you can just copy your XML into clipboard and make Edit -> Paste Special -> Paste XML As Classes in Visual Studio. Then, after cleaning up of generated code, we get the following:
[XmlRoot(ElementName = "coverageCd")]
public partial class XmlCoverageCode
{
[XmlText]
public string CoverageCode { get; set; }
[XmlElement("descriptorCd")]
public List<XmlDescriptor> Descriptors { get; set; }
}
public partial class XmlDescriptor
{
[XmlText]
public string DescriptorCode { get; set; }
[XmlElement("descriptorCdStartDate")]
public string DescriptorCodeStartDate { get; set; }
[XmlElement("descriptorCdEndDate")]
public string DescriptorCodeEndDate { get; set; }
}
This is actually the same, as you wrote in the question, but with the required attributes and changed IEnumerable to List, because XmlSerializer doesn't support the first one.
And the code snippet how to serialize/deserialize:
var serializer = new XmlSerializer(typeof(XmlCoverageCode));
var coverageCode = (XmlCoverageCode)serializer.Deserialize(xmlFileStream);
serializer.Serialize(xmlFileStream, coverageCode);
You can use xsd command for converting xml to c# class
Save the xml in the file test.xml
Run xsd.exe test.xml to generate a schema e.g. test.xsd
Run xsd.exe /c test.xsd to generate classes from the schema

Why am I getting the exception "Consider using a DataContractResolver or add any types not known statically to the list of known types"

I'm trying to serialise a object to Xml using the DataContractSerializer. I have the following classes;
[ActiveRecord(Lazy = true)]
[KnownType(typeof(RoomType))]
[DataContract]
public class Room : ActiveRecordBase<Room>
{
[PrimaryKey]
[DataMember]
public virtual Int64 RoomId { get; protected set; }
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
[Property]
[DataMember]
public virtual Int64 HotelId { get; set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
[Property]
[DataMember]
public virtual string Description { get; set; }
public static Room[] FindByHotelId(Int64 HotelId)
{
return (Room[])FindAllByProperty(typeof(Room), "HotelId", HotelId);
}
}
The RoomType class is
[ActiveRecord(Lazy = true)]
[DataContract]
public class RoomType : ActiveRecordBase<RoomType>
{
[PrimaryKey]
[DataMember]
public virtual int RoomTypeId { get; protected set; }
[Property]
[DataMember]
public virtual string Name { get; set; }
}
I use the following method to serialise the object
internal static XElement ObjectToXElement<T>(T source)
{
XDocument oXDocument = new XDocument();
try
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(source.GetType());
serializer.WriteObject(writer, source);
}
}
catch(Exception e)
{
using (var writer = oXDocument.CreateWriter())
{
// write xml into the writer
var serializer = new DataContractSerializer(oError.GetType());
serializer.WriteObject(writer, oError);
}
}
return oXDocument.Root;
}
The actual object I'm serialising is;
[KnownType(typeof(List<Room>))]
[KnownType(typeof(RoomType))]
[DataContract]
public class RoomTypeResponse
{
[DataMember]
public int Code { get; set; }
[DataMember]
public string Message { get; set; }
[DataMember]
public List<Room> Rooms { get; set; }
public RoomTypeResponse()
{
this.Rooms = new List<Room>();
}
}
But for some reason when I call the method to serialise the object I get the following exception;
Type 'Castle.Proxies.RoomTypeProxy' with data contract name
'RoomTypeProxy:http://schemas.datacontract.org/2004/07/Castle.Proxies'
is not expected. Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
If I comment out the property in Room class, it works fine
[BelongsTo("RoomTypeId")]
[DataMember]
public virtual RoomType RoomType { get; set; }
I'm not sure why I am getting the exception, because I've added the knowtype attribute for RoomType ? What am I missing, that is causing this problem.
The problem is that one type (Castle.Proxies.RoomTypeProxy) is generated at runtime, so .NET knows nothing about it. This is not an NHibernate-specific problem. If you disable lazy loading and proxy generation, the problem will go away, but I understand it might be difficult.
Other option would be to use another serializer, like BinaryFormatter, but I don't know if that will work for you.

class design: How do I create a class with nested objects?

I am currently developing a client library for connecting to Newegg using the documentation provided by Newegg and have a question on class design.
In working with various API's ( namely NetSuite and Amazon's MWS ) I come across classes that have are used like this:
recToFulfill.packageList = new ItemFulfillmentPackageList();
recToFulfill.packageList.package = new ItemFulfillmentPackage[ifitemlist.item.Length];
recToFulfill.packageList.package[i] = new ItemFulfillmentPackage();
recToFulfill.packageList.package[i].packageWeightSpecified = true;
recToFulfill.packageList.package[i].packageTrackingNumber = "trackingNumber";
The question I have is: How do I properly design the nested objects like above? I have never had to worry about this previously, so I am unsure on where to look, or start.
The bit I need to figure out looks like this ( taken from the API documentation provided):
<UpdateOrderStatusInfo>
<IsSuccess></IsSuccess>
<Result>
<OrderNumber></OrderNumber>
<SellerID></SellerID>
<OrderStatus></OrderStatus>
</Result>
</UpdateOrderStatusInfo>
All fields are type string, except order number which is an integer.
I have this currently:
public class UpdateOrderStatusInfo
{
public string IsSuccess { get; set; }
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
But the returned XML Response has Results as a parent node which to me seems like it should be represented within the class itself. Would I just do this?
public UpdateOrderStatusInfo results {get; set;}
If so, where do the child nodes go?
What I need is to be able to say is something like:
UpdateOrderStatusInfo updateInfo = new UpdateOrderStatusInfo();
if(updateInfo.IsSuccess.Equals("true")
{
Console.WriteLine(updateInfo.Results.OrderStatus);
}
Any help, or advice on where to get this information is appreciated.
Easy breezy. If it has no children, it's a scalar property. If it does, it is its own class, and referenced in the parent class accordingly. If it repeats, it's a collection, and is referenced like a class (these are complex type, not primitives). Make sure you initialize them in your constructors).
class Program
{
static void Main(string[] args)
{
var myOrder = new UpdateOrderStatusInfo();
myOrder.IsSuccess = "true";
myOrder.OrderResult.OrderNumber = 1001;
myOrder.OrderResult.OrderStatus = "Pending";
myOrder.OrderResult.SellerID = "69";
}
}
public class UpdateOrderStatusInfo
{
public string IsSuccess { get; set; }
public Result OrderResult { get; set; }
public UpdateOrderStatusInfo()
{
OrderResult = new Result();
}
}
public class Result
{
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
You need to define the Result as a separate class, called whatever you want, then add a Result property as that type. The Result class can be defined at the namespace level, or, if you are unlikely to use it anywhere else on its own, you can nest the class definition inside the UpdateOrderStatusInfo class:
public class UpdateOrderStatusInfo
{
public class UpdateOrderResult
{
public int OrderNumber { get; set; }
public string SellerID { get; set; }
public string OrderStatus { get; set; }
}
public UpdateOrderStatusInfo()
{
Result = new UpdateOrderResult();
}
public string IsSuccess { get; set; }
public UpdateOrderResult Result { get; set; }
}
The easy way is to use the xsd.exe tool.
The command xsd response.xml will generate the file response.xsd
The command xsd response.xsd /C will generate the file response.cs which contains the classes necessary to serialize/deserialize the xml posted.

Categories