I have following exercise to do ...
I shall get following xml-file ...
<?xml version="1.0" encoding="UTF-8"?>
<Mitarbeiterstatistik xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Mitarbeiter>
<Vorname>Horst</Vorname>
<Nachname>Schneider</Nachname>
<Id>1</Id>
</Mitarbeiter>
<Mitarbeiter>
<Vorname>Tanja</Vorname>
<Nachname>Lindner</Nachname>
<Id>2</Id>
</Mitarbeiter>
</Mitarbeiterstatistik>
Now I tried following steps ...
I made a class Mitarbeiter!
public class Mitarbeiter
{
private string vorname;
private string nachname;
private int id;
public Mitarbeiter()
{
}
public Mitarbeiter(string vorname, string nachname, int id)
{
this.vorname = vorname;
this.nachname = nachname;
this.id = id;
}
public string Vorname
{
get { return vorname; }
set { vorname = value; }
}
public string Nachname
{
get { return nachname; }
set { nachname = value; }
}
public int Id
{
get { return id; }
set { id = value; }
}
}
Then I made a class Mitarbeiterstatistik with a list for Mitarbeiter objects ...
[XmlRoot("Mitarbeiterstatistik")]
public class Mitarbeiterstatistik
{
private List<Mitarbeiter> list = new List<Mitarbeiter>();
[XmlArray("List")]
public List<Mitarbeiter> List
{
get { return list; }
set { list = value; }
}
}
My Main-Class looks like ...
class Program
{
static void Main(string[] args)
{
Mitarbeiterstatistik maStatistik = new Mitarbeiterstatistik();
Mitarbeiter ma1 = new Mitarbeiter("Horst", "Schneider", 1);
Mitarbeiter ma2 = new Mitarbeiter("Tanja", "Lindner", 2);
maStatistik.List.Add(ma1);
maStatistik.List.Add(ma2);
XmlSerializer serializer = new XmlSerializer(typeof(Mitarbeiterstatistik));
XmlWriter writer = XmlWriter.Create(#"D:\test.xml");
serializer.Serialize(writer, maStatistik);
writer.Close();
}
}
Now I got following result ...
<?xml version="1.0" encoding="UTF-8"?>
<Mitarbeiterstatistik xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
**<List>**
<Mitarbeiter>
<Vorname>Horst</Vorname>
<Nachname>Schneider</Nachname>
<Id>1</Id>
</Mitarbeiter>
<Mitarbeiter>
<Vorname>Tanja</Vorname>
<Nachname>Schneider</Nachname>
<Id>2</Id>
</Mitarbeiter>
**</List>**
</Mitarbeiterstatistik>
No I have an Element "List" in my Xml-file ... :-)
What can I do against my problem ...
Is there only the possibility to define only a Mitarbeiter class and NO Mitarbeiterstatistik-Class?
Maybe as following?
List<Mitarbeiter> list = new List<Mitarbeiter>();
Mitarbeiter ma1 = new Mitarbeiter("Horst", "Schneider", 1);
Mitarbeiter ma2 = new Mitarbeiter("Tanja", "Lindner", 2);
list.Add(ma1);
list.Add(ma2);
XmlSerializer serializer = new XmlSerializer(typeof(List<Mitarbeiter>), new XmlRootAttribute("Mitarbeiterstatistik"));
XmlWriter writer = XmlWriter.Create(#"D:\test.xml");
serializer.Serialize(writer, list);
writer.Close();
Or is there a chance to keep my Mitarbeiterstatistik-Class??? And disable my List-Element???
If you want to try Linq To Xml:
XDocument xDoc = new XDocument(new XElement("Mitarbeiterstatistik"));
foreach (var mitarbeiter in list)
{
xDoc.Root.Add(
new XElement("Mitarbeiter",
new XElement("Vorname" ,mitarbeiter.Vorname ),
new XElement("Nachname" ,mitarbeiter.Nachname ),
new XElement("Id" ,mitarbeiter.Id )));
}
xDoc.Save(#"d:\test.xml");
You can get out of attribute "List" (as i undestand your question correctly) using [XmlElement] with name of element you want to get instead of [XmlArray]:
[XmlRoot("Mitarbeiterstatistik")]
public class Mitarbeiterstatistik
{
private List<Mitarbeiter> list = new List<Mitarbeiter>();
[XmlElement("Mitarbeiter")]
public List<Mitarbeiter> List {get; set;}
}
Related
I do not know how to handle multiple XML Tags with the same name without doing arrays or List.
Original XML wihtout content:
`<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04">
<BkToCstmrStmt>
<GrpHdr>
<MsgId></MsgId>
<CreDtTm></CreDtTm>
<MsgPgntn>
<PgNb></PgNb>
<LastPgInd></LastPgInd>
</MsgPgntn>
</GrpHdr>
<Stmt>
<Id></Id>
<ElctrncSeqNb></ElctrncSeqNb>
<CreDtTm></CreDtTm>
<FrToDt>
<FrDtTm></FrDtTm>
<ToDtTm></ToDtTm>
</FrToDt>
<Acct>
<Id>
<IBAN></IBAN>
</Id>
<Ccy></Ccy>
<Ownr>
<Nm></Nm>
<PstlAdr>
<AdrLine></AdrLine>
<AdrLine></AdrLine>
</PstlAdr>
</Ownr>
<Svcr>
<FinInstnId>
<BICFI></BICFI>
<Nm></Nm>
</FinInstnId>
</Svcr>
</Acct>
<Bal></Bal>
<Bal></Bal>
<Ntry></Ntry>
<Ntry></Ntry>
<Ntry></Ntry>
</BkToCstmrStmt></Document>`
Now im at AdrLine. Normaly i would think its something like string[] AdrLine.
But that is not the case here. string[] would generate following:
<AdrLine><string></string><string></string></AdrLine>
I know AdrLine must be a class or struct to achiev that structure. So i tested around with that but stuck at multiple lines and value without using array or list. What i currnently have is: <PstlAdr><AdrLine /></PstlAdr> If someone can enlighten me, that would be great pleasure. The Achievment should look like this: <PstlAdr><AdrLine>Line1</AdrLine><AdrLine>Line2</AdrLine></PstlAdr>
At the <Bal></Bal><Bal></Bal> or <Ntry></Ntry><Ntry></Ntry> i have the same Issue. Normaly i would think this is in a Array, but here this isn't the case too cause of the missing Array Tags around the <Bal></Bal> or <Ntry></Ntry> tags.
Serialize method with Generictype:
public static readonly string defaultFilePath = Application.StartupPath;
public static readonly string fileFormat = "xml";
public static void SaveAs<T>(T obj, string filename, string xmlNamespace = "")
{
string filePath = defaultFilePath + "/" + filename + "." + fileFormat;
using (FileStream outFile = File.Create(filePath))
{
if (File.Exists(filePath))
{
XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType(), xmlNamespace);
xmlSerializer.Serialize(outFile, obj);
}
else { throw new FileNotFoundException("File not found", filename); }
}
}
My Testing Implementation:
BankDesign.Camt.Document document = new BankDesign.Camt.Document();
document.BkToCstmrStmt = new BkToCstmrStmt();
document.BkToCstmrStmt.GrpHdr = new BankDesign.Camt.GrpHdr(1233215, DateTime.UtcNow, new MsgPgntn(123546, false));
document.BkToCstmrStmt.Stmt = new Stmt();
document.BkToCstmrStmt.Stmt.Acct = new Acct();
AdrLine[] lines = new AdrLine[2];
AdrLine AdrLine = new AdrLine();
AdrLine.Name = "Test";
lines[0] = AdrLine;
AdrLine = new AdrLine();
AdrLine.Name = "Test2";
lines[1] = AdrLine;
document.BkToCstmrStmt.Stmt.Acct.Ownr = new Ownr("Test", new PstlAdr(lines));
FileManager.SaveAs<Document>(document, "Camt05300104", "urn:iso:std:iso:20022:tech:xsd:camt.053.001.04");
Camt05300104 Data:
<?xml version="1.0"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.04">
<BkToCstmrStmt>
<GrpHdr>
<MsgId>1233215</MsgId>
<CreDtTm>2023-02-07T10:57:07.2314082Z</CreDtTm>
<MsgPgntn>
<PgNb>123546</PgNb>
<LastPgInd>false</LastPgInd>
</MsgPgntn>
</GrpHdr>
<Stmt>
<Id>0</Id>
<ElctrncSeqNb>0</ElctrncSeqNb>
<CreDtTm>0001-01-01T00:00:00</CreDtTm>
<FrToDt>
<FrDtTm>0001-01-01T00:00:00</FrDtTm>
<ToDtTm>0001-01-01T00:00:00</ToDtTm>
</FrToDt>
<Acct>
<Id />
<Ownr>
<Nm>Test</Nm>
<PstlAdr>
<AdrLine><Name>Test</Name></AdrLine>
<AdrLine><Name>Test2</Name></AdrLine>
</PstlAdr>
</Ownr>
</Acct>
</Stmt>
</BkToCstmrStmt>
</Document>
The Camt Classes for Deserialize
//camt.053.001.04
namespace BankDesign.Camt
{
public struct PstlAdr
{
[XmlElement()]
public AdrLine[] AdrLine;
public PstlAdr(AdrLine[] adrLine)
{
AdrLine = adrLine;
}
}
public class AdrLine
{
public string Name { get; set; }
public AdrLine() { }
}
}
Seems i found a solution that worked for me.
public struct PstlAdr
{
[XmlElement()]
public AdrLine[] AdrLine;
public PstlAdr(AdrLine[] adrLine)
{
AdrLine = adrLine;
}
}
public class AdrLine
{
[XmlText()]
public string Name { get; set; }
public AdrLine() { }
}
Found at:
https://learn.microsoft.com/de-de/dotnet/api/system.xml.xmlelement?view=net-7.0 and
https://learn.microsoft.com/de-de/dotnet/api/system.xml.xmltext?view=net-7.0
I'd like to retrieve each undeclared namespaces' prefix in a Xml file on load using (where msCurrentContent is a memorystream) :
xmlCurrentDoc = new XmlDocument();
xmlCurrentDoc.Load(msCurrentContent);
For example, when loading a Xml file with the following declaration :
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="mondoc" xls:schemaLocation="mondoc.xsd">
It must retrieve the undeclared prefix xls without throwing an exception (as it does know).
What is the best way to do this ?
Thanks for your help !
This is really hacky, but you could subclass XmlNamespaceManager and add fake namespaces as you encounter unknown prefixes:
public class MyXmlNamespaceManager : XmlNamespaceManager
{
const string DefaultMissingNamespacePrefix = "http://missing.namespace.prefix.net/2014/";
private string MissingNamespacePrefix { get; set; }
private int NextMissingNamespaceIndex { get; set; }
// The dictionary consists of a collection of namespace names keyed by prefix.
public Dictionary<string, List<string>> MissingNamespaces { get; private set; }
public MyXmlNamespaceManager(XmlNameTable nameTable)
: this(nameTable, null) { }
public MyXmlNamespaceManager(XmlNameTable nameTable, string missingNamespacePrefix)
: base(nameTable)
{
this.MissingNamespacePrefix = (string.IsNullOrEmpty(missingNamespacePrefix) ? DefaultMissingNamespacePrefix : missingNamespacePrefix);
this.MissingNamespaces = new Dictionary<string, List<string>>();
}
void AddMissingNamespace(string prefix)
{
if (string.IsNullOrEmpty(prefix))
return;
string uri;
do
{
int index = NextMissingNamespaceIndex++;
uri = MissingNamespacePrefix + index.ToString();
}
while (LookupPrefix(uri) != null); // Just in case.
Debug.WriteLine(string.Format("Missing namespace \"{0}\" found, added fake namespace \"{1}\"", prefix, uri));
AddNamespace(prefix, uri);
MissingNamespaces.Add(prefix, uri);
}
public override bool HasNamespace(string prefix)
{
var result = base.HasNamespace(prefix);
if (!result)
AddMissingNamespace(prefix);
result = base.HasNamespace(prefix);
return result;
}
public override string LookupNamespace(string prefix)
{
var result = base.LookupNamespace(prefix);
if (result == null)
AddMissingNamespace(prefix);
result = base.LookupNamespace(prefix);
return result;
}
}
public static class DictionaryExtensions
{
public static void Add<TKey, TValue>(this IDictionary<TKey, List<TValue>> listDictionary, TKey key, TValue value)
{
if (listDictionary == null)
throw new ArgumentNullException();
List<TValue> values;
if (!listDictionary.TryGetValue(key, out values))
{
listDictionary[key] = values = new List<TValue>();
}
values.Add(value);
}
}
And then, to test:
string xml = #"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""no""?>
<Document xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns=""mondoc"" xls:schemaLocation=""mondoc.xsd"">
</Document>
";
XmlDocument xmlDoc;
using (var stream = new StringReader(xml))
{
var settings = new XmlReaderSettings();
settings.NameTable = new NameTable();
var manager = new MyXmlNamespaceManager(settings.NameTable);
XmlParserContext context = new XmlParserContext(null, manager, null, XmlSpace.Default);
using (var xmlReader = XmlReader.Create(stream, settings, context))
{
xmlDoc = new XmlDocument();
xmlDoc.Load(xmlReader);
}
}
string newXml;
using (var writer = new StringWriter())
{
xmlDoc.Save(writer);
newXml = writer.ToString();
}
Debug.WriteLine(newXml);
Which produces the following result:
<?xml version="1.0" encoding="utf-16" standalone="no"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="mondoc" xls:schemaLocation="mondoc.xsd" xmlns:xls="http://missing.namespace.prefix.net/2014/0">
</Document>
At least, it's not an exception. Note - only partially tested.
I have a class, lets call it, Employee, that has a list of qualifications as property
[XmlRoot("Employee")]
public class Employee
{
private string name;
private IListManager<string> qualification = new ListManager<string>();
public Employee()
{
}
[XmlElement("Name")]
public string Name
{
get
{
return name;
}
set
{
if (value != null)
{
name = value;
}
}
}
public ListManager<string> QualificationList
{
get
{
return qualification as ListManager<string>;
}
}
[XmlElement("Qual")]
public string Qualifications
{
get
{
return ReturnQualifications();
}
}
private string ReturnQualifications()
{
string qual = string.Empty;
for (int i = 0; i < qualification.Count; i++)
{
qual += " " + qualification.GetElement(i);
}
return qual;
}
public override String ToString()
{
String infFormat = String.Format("{0, 1} {1, 15}", this.name, Qualifications);
return infFormat;
}
}
}
Then I have a method that serializes the above class tom XML by taking a list of Employees as patameter, the method is generic:
public static void Serialize<T>(string path, T typeOf)
{
XmlSerializer xmlSer = new XmlSerializer(typeof(T));
TextWriter t = new StreamWriter(path);
try
{
xmlSer.Serialize(t, typeOf);
}
catch
{
throw;
}
finally
{
if (t != null)
{
t.Close();
}
}
}
This is how I call the method XMLSerialize:
controller.XMLSerialize<Employee>(opnXMLFileDialog.FileName, employeeList);
The result of the method execution returns a file and is shown below:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEmployees xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Employees>
<Name>g</Name>
</Employees>
</ArrayOfEmployees>
as you can see there is only the property name included in the file. How do I proceed from here and serialize the list of qualifications too? Any suggestionswill be greatly appreciated.
Thanks in advance.
In order to serialize a property, XmlSerializer requires that the property have both a public setter and public getter. So whatever property you choose to serialize your qualifications must have a setter as well as a getter.
The obvious solution would be for your QualificationList to have a setter as well as a getter:
public ListManager<string> QualificationList
{
get
{
return qualification as ListManager<string>;
}
set
{
qualification = value;
}
}
If for some reason this cannot be done -- perhaps because your ListManager<string> class is not serializable -- you could serialize and deserialize it as a proxy array of strings, like so:
[XmlIgnore]
public ListManager<string> QualificationList
{
get
{
return qualification as ListManager<string>;
}
}
[XmlElement("Qual")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public string[] QualificationArray
{
get
{
return Enumerable.Range(0, qualification.Count).Select(i => qualification.GetElement(i)).ToArray();
}
set
{
// Here I am assuming your ListManager<string> class has Clear() and Add() methods.
qualification.Clear();
if (value != null)
foreach (var str in value)
{
qualification.Add(str);
}
}
}
Then for the following employee list:
var employee = new Employee() { Name = "Mnemonics" };
employee.QualificationList.Add("posts on stackoverflow");
employee.QualificationList.Add("easy to remember");
employee.QualificationList.Add("hard to spell");
var list = new List<Employee>();
list.Add(employee);
The following XML is generated:
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfEmployee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Employee>
<Name>Mnemonics</Name>
<Qual>posts on stackoverflow</Qual>
<Qual>easy to remember</Qual>
<Qual>hard to spell</Qual>
</Employee>
</ArrayOfEmployee>
Is that satisfactory?
So when i try to use:
WebRequest request = WebRequest.Create("http://localhost:9998/API/GetGameById/" + ID.ToString());
WebResponse ws = request.GetResponse();
StreamReader responseStream = new StreamReader(ws.GetResponseStream());
string response = responseStream.ReadToEnd();
Game game;
using (MemoryStream Stream = new MemoryStream(UTF8Encoding.Default.GetBytes(response)))
{
XmlSerializer Serializer = new XmlSerializer(typeof(Game));
game = Serializer.Deserialize(Stream) as Game;
}
responseStream.Close();
return game;
the "game" that get's returned dose not have the same properties as the game in the xmlstring "response", it's like the :
game = Serializer.Deserialize(Stream) as Game;
creates a new instance of a game object instead of giving me the game at the specified ID
The "string response" locks like this:
<Game xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Gameboard i:nil="true" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<Id>1</Id>
<Players xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:int>1</a:int>
</Players>
<hsize>0</hsize>
<vsize>0</vsize>
<winner>0</winner>
</Game>
but the game at the return marker locks like this
<Game xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Gameboard i:nil="true" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<Id>0</Id>
<Players i:nil="true" xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays"/>
<hsize>0</hsize>
<vsize>0</vsize>
<winner>0</winner>
</Game>
Game class if it's of any help:
[DataContract(Namespace = "")]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Game
{
[DataMember]
int Id;
[DataMember]
int[] Players;
[DataMember]
int[] Gameboard;
[DataMember]
int hsize;
[DataMember]
int vsize;
[DataMember]
int winner = 0;
public Game()
{ }
public Game(int newgameID)
{
Id = ++newgameID;
}
public int GetSetId
{
get { return Id; }
set { Id = value; }
}
public int[] GetSetGameboard
{
get { return Gameboard; }
set { Gameboard = value; }
}
public int GetSetwinner
{
get { return winner; }
set { winner = value; }
}
public int[] GetPlayerList
{
get { return Players; }
}
public void AddPlayer()
{
int NewPlayer;
if (Players == null)
Players = new int[0];
List<int> temp = Players.ToList();
if (temp.Count == 0)
NewPlayer = 1;
else
{
NewPlayer = temp.Last();
NewPlayer++;
}
temp.Add(NewPlayer);
Players = temp.ToArray();
}
}
sorry if i gave you to much/little but this is my 3 or 4th post so i'm still learning just ask if your missing something
Thank you so much for taking your time and helping me out!
Have a great day!!
You may want to use a DataContractSerializer as demonstrated here instead of an XmlSerializer.
I tried with this extension method and it works a expected. The problem might be due to that the XmlSerializer does not work with private fields.
public static T DeserializeWithDataContractSerializer<T>(this string xml)
{
var dataContractSerializer = new DataContractSerializer(typeof(T));
using (var reader = new XmlTextReader( new StringReader(xml)))
{
return (T)dataContractSerializer.ReadObject(reader);
}
}
[Test]
public void GameTest()
{
string xml =
#" <Game xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<Gameboard i:nil=""true"" xmlns:a=""http://schemas.microsoft.com/2003/10/Serialization/Arrays""/>
<Id>1</Id>
<Players xmlns:a=""http://schemas.microsoft.com/2003/10/Serialization/Arrays"">
<a:int>1</a:int>
</Players>
<hsize>0</hsize>
<vsize>0</vsize>
<winner>0</winner>
</Game>";
var game = xml.DeserializeWithDataContractSerializer<Game>();
Assert.AreEqual(1,game.GetSetId);
}
I want to generate following xml output in my C# code :
<?xml version="1.0" encoding="utf-16"?>
<CallConnectReq Xmlns="urn:interno-com:ns:a9c" reqId="9" msgNb="2">
<LocalCallId>0</LocalCallId>
</CallConnectReq>
right now I am achieving this as follows:
var xnameSpace = new XmlSerializerNamespaces();
xnameSpace.Add("Xmlns", Constants.XmlNameSpaceValue);
var xmlSerializer = new XmlSerializer(objToSerialize.GetType());
var stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, objToSerialize, xnameSpace);
return stringWriter.ToString().**Replace("xmlns:","");**
But I want to remove "xmlns:" tag without using Replace() method.
Is there any way to do it?
To add just the default namespace:
var xnameSpace = new XmlSerializerNamespaces();
xnameSpace.Add("", "urn:interno-com:ns:a9c");
var ser = new XmlSerializer(typeof (CallConnectRequest));
ser.Serialize(destination, new CallConnectRequest(), xnameSpace);
with:
[XmlRoot("CallConnectReq", Namespace = "urn:interno-com:ns:a9c")]
public class CallConnectRequest {}
If you genuinely want Xmlns (which, to restate, I strongly believe is a typo of xmlns, and if not: is a bad choice in that it adds confusion), then:
var xnameSpace = new XmlSerializerNamespaces();
xnameSpace.Add("", "");
var ser = new XmlSerializer(typeof (CallConnectRequest));
ser.Serialize(destination, new CallConnectRequest {
RequestId = 9,
MessageNumber = 2,
LocalCallId = 0
}, xnameSpace);
using:
[XmlRoot("CallConnectReq")]
public class CallConnectRequest {
[XmlAttribute("Xmlns"), Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public string XmlNamespace {
get { return "urn:interno-com:ns:a9c";} set { }
}
[XmlAttribute("reqId")]
public int RequestId { get; set; }
[XmlAttribute("msbNb")]
public int MessageNumber { get; set; }
[XmlElement("LocalCallId")]
public int LocalCallId { get; set; }
}
which writes:
<?xml version="1.0" encoding="ibm850"?>
<CallConnectReq Xmlns="urn:interno-com:ns:a9c" reqId="9" msbNb="2">
<LocalCallId>0</LocalCallId>
</CallConnectReq>