How to define class structure for custom XML format - c#

Hi I have the xml format defined for which i need to serialize from my object.
My XML will look like :
<PerfCheckConsidered>
<PerfMarker PercentageIncrease="21.4171662006946" MaxValue="6650.55" MinValue="5477.438" TS_09082021093913="5961.733" TS_09082021093646="6650.55" TS_09082021093358="5477.438" TS_09082021093136="5510.135" TS_09082021092614="5768.742" PerfMarkerName="Marker1"/>
<PerfMarker PercentageIncrease="20.9911157408996" MaxValue="7371.881" MinValue="6092.911" TS_09082021093913="7371.881" TS_09082021093646="6092.911" TS_09082021093358="6345.139" TS_09082021093136="6289.624" TS_09082021092614="6782.205" PerfMarkerName="Marker1"/>
<PerfMarker PercentageIncrease="9.22930544021455" MaxValue="6183.199" MinValue="5660.751" TS_09082021093913="6183.199" TS_09082021093646="5660.751" TS_09082021093358="6000.608" TS_09082021093136="5951.08" TS_09082021092614="5870.288" PerfMarkerName="Marker1"/>
<PerfMarker PercentageIncrease="25.0711884164738" MaxValue="10075.0518" MinValue="8055.4538" TS_09082021093913="10062.1176" TS_09082021093646="10061.7749" TS_09082021093358="10075.0518" TS_09082021093136="8055.4538" TS_09082021092614="10063.9758" PerfMarkerName="Marker2"/>
<PerfMarker PercentageIncrease="13.2855819181415" MaxValue="8250.0617" MinValue="7282.5346" TS_09082021093913="8237.9737" TS_09082021093646="8245.9522" TS_09082021093358="8240.958" TS_09082021093136="8250.0617" TS_09082021092614="7282.5346" PerfMarkerName="Marker2"/>
<PerfMarker PercentageIncrease="29.7888969731873" MaxValue="50.4823" MinValue="38.8957" TS_09082021093913="50.4823" TS_09082021093646="46.8748" TS_09082021093358="38.8959" TS_09082021093136="38.8957" TS_09082021092614="40.8915" PerfMarkerName="Marker3"/>
<PerfMarker PercentageIncrease="54.5326940955576" MaxValue="16.9544" MinValue="10.9714" TS_09082021093913="11.9676" TS_09082021093646="12.9662" TS_09082021093358="16.9544" TS_09082021093136="10.9714" TS_09082021092614="10.9714" PerfMarkerName="Marker3"/>
<PerfMarker PercentageIncrease="14.028196761682" MaxValue="64.8256" MinValue="56.8505" TS_09082021093913="60.8377" TS_09082021093646="62.8326" TS_09082021093358="56.8505" TS_09082021093136="59.2591" TS_09082021092614="64.8256" PerfMarkerName="Marker3"/>
<PerfMarker PercentageIncrease="8.33604893089013" MaxValue="25.9311" MinValue="23.9358" TS_09082021093913="25.9311" TS_09082021093646="23.9358" TS_09082021093358="25.93" TS_09082021093136="25.9306" TS_09082021092614="24.9344" PerfMarkerName="Marker3"/>
</PerfCheckConsidered>
Here in above XML, TagName like TS_09082021093913,TS_09082021093646,TS_09082021093136,TS_09082021092614 are not known in compile time, These are TimeStamp Names formed from files names present in local disk.
How should i define my object structure to fill data of this kind
Current class looks like (From this structure i cannot get the desired xml format)
public class PerfCheck
{
public string PerfMarkerName { get; set; }
public string PercentageIncrease { get; set; }
public string MaxValue { get; set; }
public string MinValue { get; set; }
public string Marker { get; set; }
public List<TimeStampToInstances> TimeStampToMarkerValues { get; set;
}
[Serializable]
public class TimeStampToInstances
{
public string TimeStamp { get; set; }
public List<double> InstancesValues { get; set; }
}
Currently i am using my Custom logic to form the XML, But i am told to use normal xml serialization and try to modify the class structure to accommodate xml format
Is this possible ?

Thanks all for your valuable inputs.
I was able to achieve required XML format by using dynamic object.
I Serialized my dynamic object to intermediate XML, From which i created my XML in required format.

Related

Serializing ArrayList outputs ArrayOfAnyType

I do have a problem with serializing a ArrayList. Most propably I use wrong XML Attributes for it since I when I changed them it would not even serialize it and got errors like 'The type may not be used in this context.'
I need to use a non generic ArrayList. On adding [XmlArray("LineDetails")] made this code to run but the output is not correct, it should give me the LineDetails structure. Any idea how to fix this?
This is a part of a whole xml like Document > Header > LineCollection > LineDeatails.
The problem is only with this details if I use a standard string field it is ok but the range of the colletion if changing with every document.
[XmlType(TypeName = "LineCollection")]
public class LineCollection
{
public String LineCount{ get; set; }
// [XmlElement(ElementName = "LineDetails")]
[XmlArray("LineDetails")]
public ArrayList LineDetails{ get; set; }
}
public class LineDetails: ArrayList
{
public String LineNum{ get; set; }
public String ItemId{ get; set; }
public String ItemName{ get; set; }
//... only strings
}
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding => new UTF8Encoding(false);
}
public string Serialize()
{
// var xmlserializer = new XmlSerializer(this.GetType());
var xmlserializer = new XmlSerializer(this.GetType(), new Type[] { typeof(LineDetails) });
var Utf8StringWriter = new Utf8StringWriter();
var xns = new XmlSerializerNamespaces();
xns.Add(string.Empty, string.Empty);
using (var writer = XmlWriter.Create(Utf8StringWriter))
{
xmlserializer.Serialize(writer, this, xns);
return Utf8StringWriter.ToString();
}
}
And the incorrect output of this...
<LineColletion>
<LineDetails>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="ArrayOfAnyType"/>
</LineDetails>
</LineColletion>
it should be like this
<LineColletion>
<LineDetails>
<LineNum>1</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</LineDetails>
<LineDetails>
<LineNum>2</LineNum>
<ItemId>Item_232100000</ItemId>
<ItemName>TheItemName0</ItemName>
</LineDetails>
<LineDetails>
<LineNum>3</LineNum>
<ItemId>Item_23217777</ItemId>
<ItemName>TheItemName7</ItemName>
</LineDetails>
</LineColletion>
Now the wrong xml looks like this...
<LineDetails>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="LineDetails">
<LineNum>1</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</anyType>
<anyType xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" p5:type="LineDetails">
<LineNum>2</LineNum>
<ItemId>Item_2321</ItemId>
<ItemName>TheItemName</ItemName>
</anyType>
</LineDetails>
You may generate the required XML by modifying your data model as follows:
[XmlType(TypeName = "LineColletion")] // Fixed: TypeName. But do you want LineColletion (misspelled) or LineCollection (correctly spelled)? Your XML shows LineColletion but your code used LineCollection.
public class LineCollection
{
public String LineCount{ get; set; }
[XmlElement("LineDetails", typeof(LineDetails))] // Fixed -- specify type(s) of items in the ArrayList.
public ArrayList LineDetails{ get; set; }
}
public class LineDetails // Fixed: removed inheritance from ArrayList.
{
public String LineNum{ get; set; }
public String ItemId{ get; set; }
public String ItemName{ get; set; }
//... only strings
}
Notes:
Your model makes LineDetails inherit from ArrayList. XmlSerializer will never serialize collection properties, it will only serialize collection items. In order to serialize it correctly, I removed the inheritance since you don't seem to be using it anyway.
If you really need LineDetails to inherit from ArrayList, you will need to implement IXmlSerializable or replace it collection with a DTO.
Implementing IXmlSerializable is tedious and error-prone. I don't recommend it.
Your LineDetails collection is serialized without an outer wrapper element. To make the serializer do this, apply XmlElementAttribute to the property.
ArrayList is an untyped collection, so you must inform XmlSerializer of the possible types it might contain. You have two options:
Assigning a specific type to a specific element name by setting XmlElementAttribute.Type (or XmlArrayItemAttribute.Type), OR
Adding xsi:type attributes by informing the serializer of additional included types. You are doing this by passing them into the constructor, which is why you are seeing the p5:type="LineDetails" attribute.
Since you don't want the attributes, you need to set the element name by setting the type like so:
[XmlElement("LineDetails", typeof(LineDetails))]
The XML element corresponding to your LineCollection is named <LineColletion>. Note that the spelling is inconsistent. You will need to set [XmlType(TypeName = "LineColletion")] to the name you actually want.
Demo fiddle here.

Wrap with multiple XML tags without create diferent classes C#

I have the class
public class TestModel
{
public int Number { get; set; }
public string Text { get; set; }
}
which is converted with the following xml
<TestModel>
<Number>2</Number>
<Text>text</Text>
</TestModel>
But i need to wrap this with multiples xml tags
<Wrapper1>
<Wrapper2>
<TestModel>
<Number>2</Number>
<Text>text</Text>
</TestModel>
</Wrapper2>
</Wrapper1>
Is there any attribute to be able to put this type of wrappers? Like
[XElementWrapper("Wrapper1", "Wrapper2")]
public class TestModel
{
..
}
I dont want to create dummy classes only to contains the wrappers or create the xml element by code
Yes, you can make it by implementing IXmlSerializable

How to ignore data in JSON.NET that changes between returns

I'm parsing JSON data return by a third party.
I have my class generated with JSON2CSharp which works for the first sample we received. I tweaked it to have some JsonProperty settings so that it doesn't require certain properties that are not always present.
Now I received more samples and one of the datablobs changed format
from needing
public Translations Translations { get; set; }
to
public List<Translations> Translations { get; set; }
The blob however is information we do not need, for both performance and not having to deal with that and other pieces of information we do not need changing format, it would be ideal to just ignore it when deserializing it.
Now the real question is, should "JsonIgnore" just ignore the entire blob of data irregardless if it is in a different format then defined in the class? Or do I have to program around that?
So if I do
[JsonIgnore]
public string Translations { get; set; }
will it also ignore Translations when it gets sent a list or an object?
Can I use the same syntax with JsonIgnore as I can with JsonProperty and just say
[JsonIgnore(PropertyName = "translations")]
or does JsonIgnore just toss out anything it receives?
Additionally question:
Is it convention that when there are no translations, I get:
"translations":[]
and when there are translations I get:
"translations":{"CA":"blabla","DD":"C : blablah"}
Or is this likely a bug in the third party's website?
ADDED:
1: The Translations can switch between string, list and object between every fetch of the JSON.
2: For using DataMembers ignoring everything I don't actually need, in a class with subclasses, do I have to tell it that the subclass is [DataMember] or the subclasses properties are [DataMember]?
I would explicitly specify exactly the properties I wanted serialized/deserialized in my data class using DataContractAttribute and DataMemberAttributes for the members you actually want to deserialize.
This is opt in, so no attempt is made to shoehorn anything extra in your JSON into your data class, and anything extra in your data class doesn't show up in serialized JSON.
So assuming your class right now looks like this:
class MyData {
// Old member
// public Translations Translations { get; set; }
public List<Translation> Translations { get; set; }
public string ThisShouldBeSerialized { get; set; }
}
You can change it so things that you want serialized are explicitly marked as such, and anything not marked for serialization is ignored by default:
[DataContract]
class MyData {
// Old member
// public Translations Translations { get; set; }
public List<Translation> Translations { get; set; }
[DataMember]
public string ThisShouldBeSerialized { get; set; }
}
And then:
var myJSON = #"{
'ThisShouldBeSerialized': 'test',
'Translations': {
'Some' : 'Strange',
'Data' : 'Blob'
}
}";
var result = JsonConvert.DeserializeObject<MyData>(myJSON);

deserializing array always returns null for the element but works with attributes

I've got a problem when deserializing partially works. When I have an xml node with attributes, all the attribute values get loaded correctly into my class but when I use elements, it just returns null.
I have the following stored in an xml file:
<?xml version="1.0" encoding="utf-8"?>
<email>
<from>admin#company.com</from>
<recipients>
<recipient>user1#company.com</recipient>
<recipient>user2#company.com</recipient>
</recipients>
<subject>Test subject</subject>
<body>Test body</body>
<attachments>
<attachment>c:\test.txt</attachment>
<attachment>c:\test1.txt</attachment>
<attachment>c:\test2.txt</attachment>
</attachments>
</email>
My main class is defined as follows:
[Serializable]
[XmlRoot("email")]
public class EmailNotification
{
[XmlElement("from")]
public string From { get; set; }
[XmlElement("subject")]
public string Subject { get; set; }
[XmlElement("body")]
public string Body { get; set; }
[XmlArray("recipients")]
[XmlArrayItem("recipient")]
public List<EmailNotificationRecipient> Recipients { get; set; }
[XmlArray("attachments")]
[XmlArrayItem("attachment")]
public List<EmailNotificationAttachment> Attachments { get; set; }
public EmailNotification()
{
this.Attachments = new List<EmailNotificationAttachment>();
this.Recipients = new List<EmailNotificationRecipient>();
}
}
Here is my recipient class:
[Serializable]
public class EmailNotificationRecipient
{
public EmailNotificationRecipient()
{
}
[XmlElement("recipient")]
public string Recipient { get; set; }
}
I'm not going to bother displaying the attachment class as it is identical in regards to its definition as the recipient class.
All the simple elements are being filled correctly and my array is being build but all the elements are null inside it.
When debugging, I can see that my arrays have 2 items for the recipients and 3 items for the attachments but when I check the value inside, each of the array item are null.
I've also added a constructor the EmailNotificationRecipient class and set a breakpoint on it and it hits my breakpoint every time for each defined recipient.
With the above 2 points, it would make you believe that everything is ok with my class definitions or it would not manage to find the correct number of elements but as mentioned, all objects within my array are set to null even though the correct number of objects are created.
Originally I had the XmlArrayItem defined with the type:
[XmlArrayItem("recipient", typeof(EmailNotificationRecipient))]
public List<EmailNotificationRecipient> Recipients { get; set; }
I removed it to see if it would make any difference, but to no avail.
What am I missing? This is driving my bonkers!!!
Thanks.
UPDATE
See answer below from #NoIdeaForName.
The way I currently have it defined would have meant that in order for it to return a value, my xml should have looked like:
<recipients>
<recipient><recipient></recipient></recipient>
</recipients>
as my array is expecting a class recipient when in fact all I have within each recipient node is a string, so the array should have been defined as follows:
[XmlArray("recipients")]
[XmlArrayItem("recipient")]
public List<string> Recipients { get; set; }
Now on the other hand, if each of my recipients in my xml were defined like this:
<recipients>
<recipient>
<fname><fname>
<lname><lname>
</recipient>
</recipients>
then it would make sense to define a separate class the way I had it i.e. EmailNoticiationRecipient, but instead of having recipient as a property, you would have fname and lname.
Thanks again #NoIdeaForName :)
as you said you are doing
[XmlArray("attachments")]
[XmlArrayItem("attachment")]
public List<EmailNotificationAttachment> Attachments { get; set; }
in the EmailNotification class and your Attachments class is
[Serializable]
public class EmailNotificationRecipient
{
public EmailNotificationRecipient()
{
}
[XmlElement("recipient")]
public string Recipient { get; set; }
}
that means your xml should look like:
<recipients>
<recipient><recipient>user1#company.com</recipient></recipient>
<recipient><recipient>user2#company.com</recipient></recipient>
</recipients>
because in every recipient tag there is a class and in every recipient class should be a property name(tag as) recipient
the best way to check it would be to create a class with values and serialize them and see how the output looks like

Serialization of array of xml elements already within a xmlAny Element

<Fruits>
<FruitName>Amla</FruitName>
<FruitPrice>10 US DOLLARS</FruitPrice>
<Origin>
<Country>INDIA</Country>
<NativeName>GOOSEBERRY</NativeName>
<Availability>PLENTY</Availability>
</Origin>
<OtherInfo>
<FiberPercentage>1.11</FiberPercentage>
<MagnesiumPercentage>0.02</MagnesiumPercentage>
</OtherInfo>
While De serializing the above XML structure, I use something like,
Xml
XmlElement("FruitsList")]
public List<Fruits> FruitsImport { get; set; }
In Fruits Class, I have something like:
[XmlAnyElement]
public List<XmlElement> FruitElements { get; set; }
[XmlElement("Origin")]
public List<XmlElement> FruitOrigin { get; set; }
[XmlElement("OtherInfo")]
public List<XmlElement> OtherInfo { get; set; }
FruitElement retrieves the FruitName and FruitPrice.
FruitOrigin retrives Country Info alone.
OtherInfo retrives FiberPercentage alone.
Any ideas on how to get all the info under <Origin> and <OtherInfo> tags ?
Because you have elements nested in both <origin> and <otherinfo> tags, you need to define a class for them as well when performing deserialization.
[XmlElement("Origin")]
public List<Origins> FruitOrigin { get; set; }
You would define the origin class the same way as you did for fruit class.
(The skeleton of Origin class would be something as below:
[Serializable]
public class Origin
{
[XmlAnyElement]
public List<XmlElement> OriginElements { get; set; }
}
)
The best way is to write XSD schema for the XML and generate class from XSD schema using XSD.exe, this way you can validate that always XML and class matches to XSD and never have issue with Serialization/ Deserialization.
You can create batch file like below to generate class from xsd schema:
del Configuration.AutoGenerated.cs
"%ProgramFiles%\Microsoft SDKs\Windows\v6.0A\bin\xsd" Test.Configuration.xsd /c /n:Test.Configuration
rename Test_Configuration.cs Configuration.AutoGenerated.cs
pause
You can see more usage examples at above link for XSD.exe.
It will generate partial classes so you can easily extend it further if required.

Categories

Resources