I'm making some test to use it.
I have the following xml:
<?xml version="1.0"?>
<test xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ma>233</ma>
<ma>2333</ma>
</test>
I have this class to deserialize it:
[Serializable]
public class test
{
public string ma { get; set; }
}
It does contains the first element. Now I want both so I try setting an array
[Serializable]
public class test
{
public string[] ma { get; set; }
}
However setting an array I have now 0 result in ma variable, while I at least have the first one when it is not an array.
I found this answer Using XmlSerializer with an array in the root element, but he used another logic... I'd like to keep using [Serializable]
You have to indicate that the array doesn't have a separate xml element to wrap its items, but that the array items appear directly under the <test> element:
public class test
{
[XmlElement]
public string[] ma { get; set; }
}
PS. sometimes it's hard to get the mapping right - I usually fill in a class with test data and serilalize it, examining what XmlSerializer makes of that usually clears up what's going on.
The answer you found provides the information you need.
[Serializable] doesn't help you because it isn't used by XmlSerializer, see Why doesn't the XmlSerializer need the type to be marked [Serializable]?
Related
I'm having issues deserializing XML which I can't quite figure out. Mainly, I'm trying to avoid having to nest classes when they're inside a list but not succeeding. For example:
[XmlRoot]
[Serializable]
public class Foo
{
[XmlElement("Bar")]
public BarElement Bar = new BarElement();
public class BarElement
{
[XmlElement("MoreBars")]
public List<MoreElement> MoreBars = new List<MoreElement>();
}
[XmlRoot("More")]
[Serializable]
public class MoreElement
{
[XmlAttribute("Attribute")]
public string Attribute { get; set; }
[XmlText]
public string Value { get; set; }
}
}
Corresponds to:
<Foo>
<Bar>
<MoreBars>
<More Attribute=""></More>
<More Attribute=""></More>
<More Attribute=""></More>
<More Attribute=""></More>
</MoreBars>
</Bar>
</Foo>
This almost works... but not quite. By adding XmlRoot to MoreElement, I'm trying to avoid the necessity of having to create a new class named "MoreBarsElement" that contains only a List of MoreElements, since it's already quite a chore to access "Foo.Bar.MoreBars.Value". Is this possible? If so, how would I do it?
Answering my own question - looks like I needed
[XmlArray("MoreBars"), XmlArrayItem(typeof(MoreElement), ElementName = "More")]
On the array item declaration. It works now, hurray!
I am retrieving and successfully deserializing an xml string from a database table column for the known element names (these do not change) but there are also some nested XML Elements called "Other Attributes" which are not always going to be known. I'm having some trouble deserialising these unknown element names to a generic list so I can display them in html once deserialised.
The XML is as follows:
<Detail>
<DetailAttributes>
<Name>Name_123</Name>
<Type>Type_123</Type>
</DetailAttributes>
<OtherAttributes>
<SummaryKey AttributeName="SummaryKey">SummaryKey_123</SummaryKey>
<Account AttributeName="Account">Account_123</Account>
</OtherAttributes>
</Detail>
I have no problem deserialising the 'Name' and 'Type' elements and I can deserialise the 'SummaryKey' and 'Account' elements but only if I explicitly specify their element names - which is not the desired approach because the 'OtherAttributes' are subject to change.
My classes are as follows:
[XmlRoot("Detail")]
public class objectDetailsList
{
[XmlElement("DetailAttributes"), Type = typeof(DetailAttribute))]
public DetailAttribute[] detailAttributes { get; set; }
[XmlElement("OtherAttributes")]
public List<OtherAttribute> otherAttributes { get; set; }
public objectDetailsList()
{
}
}
[Serializable]
public class Detail Attribute
{
[XmlElement("Type")]
public string Type { get;set; }
[XmlElement("Name")]
public string Name { get;set; }
public DetailAttribute()
{
}
}
[Serializable]
public class OtherAttribute
{
//The following will deserialise ok
//[XmlElement("SummaryKey")]
//public string sumKey { get; set; }
//[XmlElement("Account")]
//public string acc { get; set; }
//What I want to do below is create a list of all 'other attributes' without known names
[XmlArray("OtherAttributes")]
public List<Element> element { get; set; }
}
[XmlRoot("OtherAttributes")]
public class Element
{
[XmlAttribute("AttributeName")]
public string aName { get; set; }
[XmlText]
public string aValue { get; set; }
}
When I try to retrieve the deserialised list of OtherAttribute elements the count is zero so it's not able to access the elements nested within "Other Attributes".
Can anybody help me with this please?
With concrete classes and dynamic data like this, you won't be able to lean on the standard XmlSerializer to serialize / deserialize for you - as it reflects on your classes, and the properties you want to populate simply don't exist. You could provide a class with all possible properties if your set of 'OtherAttributes' is known and finite, and not subject to future change, but that would give you an ugly bloated class (and I think you've already decided this is not the solution).
Practical options therefore:
Do it manually. Use the XmlDocument class, load your data with .Load(), and iterate the nodes using .SelectNodes() using an XPath query (something like "/Detail/OtherAttributes/*"). You will have to write the lot yourself, but this gives you complete control over the serialization / deserialization. You won't have to cover your code in (arguably superfluous!) attributes either.
Use Json.NET (http://james.newtonking.com/json), it allows for far greater control over serialization and deserialization. It's fast, has good docs and is overall pretty nifty really.
I have a class. I want to deserialize XML file into this class.
public partial class Form1 : Form
{
public string xmlFile;
public Form1()
{
InitializeComponent();
xmlFile = "Sample.xml";
}
[Serializable]
public class Application
{
public string AppName;
public string UpdateDetail;
};
[Serializable]
public class Applications
{
[XmlElement]
public Application[] Application;
};
[Serializable]
public class Pmsp_Update
{
public string OldVersion;
public string NewVersion;
public Applications Applications;
};
private void btnRead_Click(object sender, EventArgs e)
{
XmlReader reader = XmlReader.Create(xmlFile);
XmlSerializer ser = new XmlSerializer(typeof(Pmsp_Update));
Pmsp_Update pu;
using (reader = XmlReader.Create(xmlFile))
{
pu = (Pmsp_Update)ser.Deserialize(reader);
}
}
}
And here is the XML:
<Pmsp_Update>
<OldVersion>v4.0.0</OldVersion>
<NewVersion>v4.0.1</NewVersion>
<Applications>
<Application>
<AppName>SampleApp</AppName>
<UpdateDetail>sample</UpdateDetail>
</Application>
</Applications>
</Pmsp_Update>
I want to ask about attributes. I only used [Serializable] attribute and I can get the class. I don't use any [XmlElement] attribute and this didn't cause any error except one.
If I use like this I can populate the array but:
[Serializable]
public class Applications
{
[XmlElement]
public Application[] Application;
};
If I use like this I can't populate the array:
[Serializable]
public class Applications
{
public Application[] Application;
};
So, my question is this: I am not using [XmlElement] for fields and everything works well but I can't populate the array. Why couldn't I populate the array when I don't use [XmlElement] although I can populate the fields?
The easiest way to check what's going on is to create object of your class and serialize it to XML. This is what I get:
Without [XmlElement]
<?xml version="1.0" encoding="utf-16"?>
<Pmsp_Update xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OldVersion>v4.0.0</OldVersion>
<NewVersion>v.4.0.1</NewVersion>
<Applications>
<Application>
<Application>
<AppName>Test</AppName>
<UpdateDetail>test</UpdateDetail>
</Application>
</Application>
</Applications>
</Pmsp_Update>
With [XmlElement]
<?xml version="1.0" encoding="utf-16"?>
<Pmsp_Update xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<OldVersion>v4.0.0</OldVersion>
<NewVersion>v.4.0.1</NewVersion>
<Applications>
<Application>
<AppName>Test</AppName>
<UpdateDetail>test</UpdateDetail>
</Application>
</Applications>
</Pmsp_Update>
As you can see, only the second one matches your input XML, and that's why you cannot deserialize yout XML without [XmlElement] attribute - because your XML document does not match your class structure.
Empty XmlElement on top of array property prevent serializer from expecting additional element to handle array elements. That behavior is described on MSDN: XmlElementAttribute Class
If you apply the XmlElementAttribute to a field or property that
returns an array, the items in the array are encoded as a sequence of
XML elements.
In contrast if an XmlElementAttribute is not applied to
such a field or property, the items in the array are encoded as a
sequence of elements, nested under an element named after the field or
property. (Use the XmlArrayAttribute and XmlArrayItemAttribute
attributes to control how an array is serialized.)
You can achieve the same by changing your class structure to
public class Application
{
public string AppName;
public string UpdateDetail;
};
public class Pmsp_Update
{
public string OldVersion;
public string NewVersion;
public Application[] Applications;
};
<Applications> element will be added automatically for array property, so you don't need Applications class.
I hope to find a solution from you. What I need is to serialize ValidatorList class object into an xml document. How to do this?
I tried like this:
XmlSerializer _serializer = new XmlSerializer(list);
But I don't know how to make output of xml for list that has several classes.
C#
_list= new ListVal();
Type _type = typeof(ras);
_list.Add(new RequiredField
{
Property = _type.GetProperty("CustRef")
}.Add(new AsciiVal()));
_list.Add(new RequiredField
{
Property = _type.GetProperty("ctr")
}.Add(new StringLengthVal
{
min= 3,
max= 3
}));
[Serializable]
public class Field
{
public Field Next
{
get;
set;
}
public Field TypeName
{
get;
set;
}
public Field PropertyName
{
get;
set;
}
}
public class RequiredField : Field
{
//TODO
}
public class AsciiVal: Field
{
//TODO
}
public class StringLengthVal: Field
{
//TODO
}
public class ListVal: List<Field>
{
//TODO
}
I have something close, but not exactly the Xml you want. In actual fact I think you'll see that the Xml produced below makes a bit more sense than what you have.
To get you started, you control the serialization and deserialization using attributes in the System.Xml.Serialization namespace. A few useful ones to read up on are
XmlRootAttribute
XmlElementAttribute
XmlAttributeAttribute
XmlIncludeAttribute
So I mocked up some code which closely matches your own. Notice the addition of some attributes to instruct the serializer how I want the Xml to be laid out.
[XmlInclude(typeof(AsciiValidator))]
[XmlInclude(typeof(RequiredValidator))]
[XmlInclude(typeof(StringLengthValidator))]
public class FieldValidator
{
[XmlElement("Next")]
public FieldValidator Next
{
get;
set;
}
[XmlElement("PropertyName")]
public string PropertyName
{
get;
set;
}
}
public class AsciiValidator: FieldValidator
{
}
public class RequiredValidator: FieldValidator
{
}
public class StringLengthValidator: FieldValidator
{
[XmlElement]
public int MinLength{get;set;}
[XmlElement]
public int MaxLength{get;set;}
}
[XmlRoot("ValidatorList")]
public class ValidatorList : List<FieldValidator>
{
}
Point of interest; Every class inheriting FieldValidator must be added to the list of known types using XmlIncludeAttribute so the serializer knows what to do with them)
Then I created an example object map:
var test = new ValidatorList();
test.Add(
new RequiredValidator()
{
PropertyName="CustRef",
Next = new AsciiValidator()
});
test.Add(
new RequiredValidator()
{
PropertyName="CurrencyIndicator",
Next = new StringLengthValidator(){
MinLength=3,
MaxLength = 10
}
});
Finally I told the serializer to serialize it (and output the result to the console)
var ser = new XmlSerializer(typeof(ValidatorList));
ser.Serialize(Console.Out,test);
This was the result:
<?xml version="1.0" encoding="utf-8"?>
<ValidatorList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="AsciiValidator" />
<PropertyName>CustRef</PropertyName>
</FieldValidator>
<FieldValidator xsi:type="RequiredValidator">
<Next xsi:type="StringLengthValidator">
<MinLength>3</MinLength>
<MaxLength>10</MaxLength>
</Next>
<PropertyName>CurrencyIndicator</PropertyName>
</FieldValidator>
</ValidatorList>
Not a million miles away from what you wanted. There is the need to output certain things in a certain way (eg xsi:type tells the serializer how to deserialize back to the object map). I hope this gives you a good start.
Here is a live, working example: http://rextester.com/OXPOB95358
Deserialization can be done by calling the Deserialize method on the XmlSerializer.
For example, if your xml is in a string:
var ser = new XmlSerializer(typeof(ValidatorList));
var test = "<..../>" // Your Xml
var xmlReader = XmlReader.Create(new StringReader(test));
var validatorList = (ValidatorList)ser.Deserialize(xmlReader);
There are many overrides of Deserialize which take differing inputs depending if the data is in a Stream an existing reader, or saved to a file.
You have to decorate the class that contains the _validators field with the KonwnType attribute
[Serializable]
[KwownType(typeof(RequiredFieldValidator)]
[KwownType(typeof(AsciValidator)]
public class MySerialisableClass
I have several SO answers that detail how to serialize objects using XML. I'll provide links below.
However, since you're looking for a rather simple serialization of your object, you may want to read up on the DataContractSerializer. It's much less complicated than the old .NET 1.x XML Serialization.
Here's the list of SO answers:
Omitting all xsi and xsd namespaces when serializing an object in .NET?
XmlSerializer: remove unnecessary xsi and xsd namespaces
Suppress xsi:nil but still show Empty Element when Serializing in .Net
Using XmlAttributeOverrides on Nested Properties
Even though many of these have to do with XML serialization and namespaces, they contain complete examples of serializing an object to XML using .NET 1.x XML Serialization.
I am using the XmlSerializer, and was wondering if there is any way, using overrides or something to that effect to get the XmlSerializer to output the types of some nodes.
My problem is that I have serialized a byte array.
class MyClass {
public string Name { get; set; }
public byte[] Bytes { get; set; }
}
I am consuming the xml in a generic service.
The service collects the xml as .
<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>
Is there any way to either generate an xsd at runtime, or somehow output something like this.
I cannot change the class I am serializing, but I can apply overrides to the serializer or in some other way control the serialization.
<Bytes xsi:type='BinaryOfSomeKind'>BlahBlah</Bytes>
I need to know that the data is binary somehow.
Thanks
Craig.
If your class is supplied by a third party then you know your properties and property types and you can deduce your XML and XSD from it. You can create your XSD manually or with the help of a XML tool for example XMLSpy (not free BTW) or XMLFox which is free of charge.
If you know the xml is going to be in that format that you put in the question and you have your class ready you can decorate it as such for it to be deserialized.
The Deserialization class:
[XmlTypeAttribute]
[XmlRootAttribute("MyClass")]
public class MyClass
{
[XmlElementAttribute("Name")]
public string Name { get; set; }
[XmlElementAttribute("Bytes")]
public byte[] Bytes { get; set; }
}
The Deserialzation Method
public static object Deserialize(string xml)
{
var deserializer = new System.Xml.Serialization.XmlSerializer(typeof(MyClass));
using (var reader = XmlReader.Create(new StringReader(xml)))
{
return (MyClass)deserializer.Deserialize(reader);
}
}
The Main Method
static void Main(string[] args)
{
string xml = #"<MyClass>
<Name>Test</Name>
<Bytes>U2NhcnkgQnVnZ2Vy</Bytes>
</MyClass>";
MyClass obj = (MyClass)Deserialize(xml);
Console.ReadKey();
}
Make sure to add the following using statements:
using System.Xml.Serialization;
using System.Xml;
It deserialized it into an obj with "Test" as the byte array.
If you generate the XSD at run time, then theres no way you can know what properties there are and it would be down to using reflection to test for specific properties, and then subsequently finding out what types they may be, is this what your after?