is it possible to partially (de)/serialize an object from/into a string?
class Foo
{
Bar Bar{get;set;}
string XmlJunkAsString{get;set;}
}
so ultmately, we would want the string below to work...
<Foo><Bar></Bar><XmlJunkAsString><xml><that/><will/><not/><be/><parsed/></xml></XmlJunkAsString></Foo>
and ultimately we could find the contents of Foo.XmlJunkAsString to contain the string
<xml><that/><will/><not/><be/><parsed/></xml>
and vice-versa would occur where the xml above would be generated when this particular instance of Foo is serialized.
possible?
I was hoping that [XmlText] would work, but it seems to get escaped; you could implement IXmlSerializable, but that is very tricky. The following is ugly, but gives the right result (although you might get some xml whitespace differences)
using System;
using System.ComponentModel;
using System.Xml;
using System.Xml.Serialization;
public class Bar { }
public class Foo
{
public Bar Bar { get; set; }
[XmlIgnore]
public string XmlJunkAsString { get; set; }
[XmlElement("XmlJunkAsString"), Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public XmlElement XmlJunkAsStringSerialized
{
get
{
string xml = XmlJunkAsString;
if (string.IsNullOrEmpty(xml)) return null;
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
set
{
XmlJunkAsString = value == null ? null : value.OuterXml;
}
}
}
static class Program {
static void Main()
{
var obj = new Foo
{
Bar = new Bar(),
XmlJunkAsString = "<xml><that/><will/><not/><be/><parsed/></xml>"
};
var ser = new XmlSerializer(obj.GetType());
ser.Serialize(Console.Out, obj);
}
}
Related
I have the following situation:
4 methods that return XML
Have to create one big XML with these 4 XML pieces on an array
A small example of this would be the following:
<garage>
<owner>daniel</owner>
<cars>
<XmlElement>
<plate>ABC123</plate>
</XmlElement>
<XmlElement>
<plate>DSC563</plate>
</XmlElement>
<XmlElement>
<plate>AIO789</plate>
</XmlElement>
<XmlElement>
<plate>IUE692</plate>
</XmlElement>
</cars>
</garage>
I have an array of plate and want to inject that on the car which is an System.Xml.XmlElement[]
The problem is, I can't seem to find a way to get rid of the XmlElement element wrapping my objects, I just wanted it to be like:
<garage>
<owner>
<name>daniel</name>
</owner>
<cars>
<plate>ABC123</plate>
<plate>DSC563</plate>
<plate>AIO789</plate>
<plate>IUE692</plate>
</cars>
</garage>
I've tried playing around with the Xml Attributes but couldn't get exactly what I wanted.
Can someone please give me a hand?
here's the working code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Xunit;
using Xunit.Abstractions;
namespace TestsProjec.XML
{
public class Tests
{
private readonly ITestOutputHelper _testOutputHelper;
public Tests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}
[Fact]
public void InjectingXml()
{
var serializer = new XmlSerializer(typeof(Garage));
var cars = new List<XmlElement>();
cars.Add(StringToXmlElement("<plate>ABC123</plate>"));
cars.Add(StringToXmlElement("<plate>DSC563</plate>"));
cars.Add(StringToXmlElement("<plate>AIO789</plate>"));
cars.Add(StringToXmlElement("<plate>IUE692</plate>"));
string fullXml;
var entity = new Garage()
{
owner = "Daniel",
cars = cars.ToArray()
};
using (MemoryStream ms = new MemoryStream())
{
using (XmlWriter tw = XmlWriter.Create(ms))
{
serializer.Serialize(tw, entity);
try
{
byte[] tmp = new byte[ms.Length - 3];
ms.Position = 3; //to skip the UTF-8 preamble
ms.Read(tmp, 0, (int)ms.Length - 3);
fullXml = Encoding.UTF8.GetString(tmp);
}
catch
{
fullXml = null;
}
}
}
_testOutputHelper.WriteLine(fullXml);
}
public XmlElement StringToXmlElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
public class Garage
{
public string owner { get; set; }
public System.Xml.XmlElement[] cars { get; set; }
}
}
If you use XElement, instead of XmlElement, you could get output you want, with XmlAnyElement Attribute:
public class Garage
{
public string owner { get; set; }
[XmlAnyElement]
public XElement[] cars { get; set; }
}
Your method StringToXmlElement will look like:
public XElement StringToXmlElement(string xml)
{
return XElement.Parse(xml);
}
An option is to use XmlSerializer and have class that represent your xml
class Program
{
static void Main(string[] args)
{
XmlSerializer serializer = new XmlSerializer(typeof(Garage));
var garage = new Garage
{
Owner = new Owner { Name = "Daniel" },
Cars = new List<string>
{
"ABC123",
"DSC563",
"AIO789",
"IUE692",
}
};
serializer.Serialize(File.Create("file.xml"), garage);
}
}
[Serializable]
public class Garage
{
[XmlElement("Owner")]
public Owner Owner;
[XmlArrayItem("Plate")]
public List<string> Cars;
}
[Serializable]
public class Owner
{
[XmlElement("Name")]
public string Name;
}
I don't know if the topic is correct, if not please correct. So far i am not sure what to search for my problem so maybe the question has already been answered before.
Currently i have the following class (as example):
[Serializable]
public class Sample
{
public string Something { get; set; }
public List<Parameter> Parameters { get; set; }
}
[Serializable]
public class Parameter
{
public string Name { get; set; }
public string Value { get; set; }
}
This structure i have to serialize to the following XML:
<Sample>
<Something>1234512345112345</Something>
<Parameters>
<Name>Value</Name>
<Name>Value</Name>
</Parameters>
</Sample>
So the XML should contain the property value of the attribute "Name" as XML-Element Name.
Update 20.05.2015
I have the following XML content:
<?xml version="1.0" encoding="utf-16" ?>
<ProcessData>
<ID>123456</ID>
<IDYTPE>BASEPLATE</IDYTPE>
<State>FAIL</State>
<Recipe>654321</Recipe>
<ProcessDataParameter>
<test_0>0</test_0>
<test_1>12,34</test_1>
<test_2>24,68</test_2>
<test_3>37,02</test_3>
<test_4>49,36</test_4>
<test_5>61,7</test_5>
</ProcessDataParameter>
</ProcessData>
When i try to use the following code to deserialize:
public void ReadXml(XmlReader reader)
{
reader.ReadStartElement("ProcessData");
this.Id = reader.ReadElementString("ID");
this.IdType = reader.ReadElementString("IDYTPE");
this.State = reader.ReadElementString("State");
this.Recipe = reader.ReadElementString("Recipe");
reader.ReadStartElement("ProcessDataParameter");
this.ProcessDataParameter = new List<ProcessDataParameter>();
var subTree = reader.ReadSubtree();
while (subTree.Read())
{
if (subTree.NodeType == XmlNodeType.Text)
{
var nm = subTree.LocalName;
//Parameters.Add(new Parameter { Name = nm, Value = subTree.Value });
}
}
reader.ReadEndElement();
}
Everything gets read out fine expect the process data parameters.
It seems like the subTree.Read() just reades the element out of the XML content instead of all elements contained in the .
In the while loop the reader goes through the following values (debuged)
test_0 (start tag)
0 (value between the tag)
test_0 (end tag
and then out of the while.
Seems like the reader sees the as an subtree.
Further only the 0 - value gets recognized as XmlNodeType.Text
You could implement IXmlSerializable and create your own custom serialization behaviour for your Sample class. So in your case something like this should work
[Serializable]
public class Sample : IXmlSerializable
{
public string Something { get; set; }
public List<Parameter> Parameters { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
XmlDocument doc = new XmlDocument();
doc.Load(reader);
Something = doc.SelectSingleNode(#"/Sample/Something").FirstChild.Value;
var parameters = doc.SelectSingleNode(#"/Sample/Parameters");
if (parameters.HasChildNodes)
{
Parameters = new List<Parameter>();
foreach (XmlElement childNode in parameters.ChildNodes)
{
Parameters.Add(new Parameter {Name = childNode.LocalName, Value = childNode.FirstChild.Value});
}
}
}
public void WriteXml(XmlWriter writer)
{
writer.WriteElementString("Something", this.Something);
writer.WriteStartElement("Parameters");
foreach (var parameter in Parameters)
{
writer.WriteElementString(parameter.Name, parameter.Value);
}
writer.WriteEndElement();
}
}
Updated to include ReadXml implementation for deserialization
I'm not quite sure if the ReadXml is complete as I can't test this now, you might have to tweak it a bit for the Parameters
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
Sample sample = new Sample(){
Something = "1234512345112345",
Parameters = new List<Parameter>(){
new Parameter(){
Name = new List<string>(){"Value", "Value"}
}
}
};
XmlSerializer serializer = new XmlSerializer(typeof(Sample));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, sample);
writer.Flush();
writer.Close();
writer.Dispose();
XmlSerializer xs = new XmlSerializer(typeof(Sample));
XmlTextReader reader = new XmlTextReader(FILENAME);
Sample newSample = (Sample)xs.Deserialize(reader);
}
}
[XmlRoot("Sample")]
public class Sample
{
[XmlElement("Something")]
public string Something { get; set; }
[XmlElement("Parameters")]
public List<Parameter> Parameters { get; set; }
}
[XmlRoot("Parameters")]
public class Parameter
{
[XmlElement("Name")]
public List<string> Name { get; set; }
}
}
I have a class need to be serialized.
namespace serializedobject
{
[DataContract]
public class Class1
{
string string1_;
string string2_;
EntityA entity_;
[DataMember]
public string string3
{
get { return string1_; }
set { string1_ = value; }
}
[DataMember]
public string string2
{
get { return string2_; }
set { string2_ = value; }
}
[DataMember]
public EntityA Entity
{
get { return entity_; }
set { entity_ = value; }
}
public static Class1 FromXML(string desc)
{
using (MemoryStream ms = new MemoryStream())
{
StreamWriter writer = new StreamWriter(ms);
writer.Write(desc);
writer.Flush();
ms.Seek(0, 0);
DataContractSerializer ser = new DataContractSerializer(typeof(Class1));
return (Class1)ser.ReadObject(ms);
}
}
public string ToXML()
{
using (MemoryStream ms = new MemoryStream())
{
DataContractSerializer ser = new DataContractSerializer(typeof(Class1));
ser.WriteObject(ms, this);
ms.Seek(0, 0);
StreamReader reader = new StreamReader(ms);
return reader.ReadToEnd();
}
}
}
[DataContract]
public class EntityA
{
string name_;
[DataMember]
public string Name
{
get { return name_; }
set { name_ = value; }
}
}
}
it is works fine with FromXML and ToXML. one of serialized context like:
<Class1 xmlns="http://schemas.datacontract.org/2004/07/serializedobject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Entity><Name>az</Name></Entity><string2 i:nil="true"/><string3>test</string3></Class1>
Later I need to move class EntityA to another namespace "outside", now the serialized context like:
<Class1 xmlns="http://schemas.datacontract.org/2004/07/serializedobject" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Entity xmlns:a="http://schemas.datacontract.org/2004/07/outside"><a:Name>az</a:Name></Entity><string2 i:nil="true"/><string3>test</string3></Class1>
but now the serialized xml which created before change namespace can't be deserialized correctly. I guess this is because of for class "EntityA" changed namespace (xmlns:a added).
does anybody run into the problem before? any suggestion?
You can stop the namespace being added to the XML by specifying [DataContract(Namespace="")]. This relies on you setting that attribute BEFORE you save any xml code.
You can use this approach only if you have not already serialized any data, so this is the approach you would use when first designing a class to be serialized.
(If you have already got serialized data that you must deal with, see the second part of my answer below.)
This code sample has the two classes called Demo in two different namespaces, Test1 and Test2.
We serialize the code using the class from one namespace, and deserialize it using the class from the other namespace:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
namespace ConsoleApp1
{
namespace Test1
{
[DataContract(Namespace="")]
public sealed class Demo
{
[DataMember]
public string Value { get; set; }
}
}
namespace Test2
{
[DataContract(Namespace="")]
public sealed class Demo
{
[DataMember]
public string Value { get; set; }
}
}
sealed class Program
{
private void run()
{
string filename = Path.GetTempFileName();
var demo1 = new Test1.Demo {Value = "DEMO"};
ToFile(filename, demo1);
var demo2 = FromFile<Test2.Demo>(filename);
Console.WriteLine(demo2.Value);
}
public static void ToFile(string filename, object obj)
{
DataContractSerializer serializer = new DataContractSerializer(obj.GetType());
using (var streamWriter = File.CreateText(filename))
using (var xmlWriter = XmlWriter.Create(streamWriter, new XmlWriterSettings{Indent = true}))
{
serializer.WriteObject(xmlWriter, obj);
}
}
public static T FromFile<T>(string filename)
{
DataContractSerializer serializer = new DataContractSerializer(typeof(T));
using (var textReader = File.OpenText(filename))
using (var xmlReader = XmlReader.Create(textReader))
{
return (T)serializer.ReadObject(xmlReader);
}
}
[STAThread]
static void Main(string[] args)
{
new Program().run();
}
}
}
If you have already serialized data without the Namespace="" attribute, then you will need instead to apply the appropriate namespace to the new class:
namespace Test1
{
[DataContract]
public sealed class Demo
{
[DataMember]
public string Value { get; set; }
}
}
namespace Test2
{
// Note the namespace includes both nested namespaces, i.e. ConsoleApp1.Test1
[DataContract(Namespace="http://schemas.datacontract.org/2004/07/ConsoleApp1.Test1")]
public sealed class Demo
{
[DataMember]
public string Value { get; set; }
}
}
I have this xml that return of a web service:
<return>
<LuckNumber>
<Number>00092</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00093</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00094</Number>
<CodError>00</CodError>
<Serie>019</Serie>
<Number>00095</Number>
<CodError>00</CodError>
<Serie>019</Serie>
</LuckNumber>
How Can I parse this XML to a typed object using annotations?
I Tried it, but doesn't work:
protected T ProccessResult<T>(string result) {
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (StringReader reader = new StringReader(result))
{
var resultDeserialize = (T)(serializer.Deserialize(reader));
return resultDeserialize;
}
}
ProccessResult<List<GenerateNumberList>>(STRING_XML_ABOVE)
CLASS TO PARSE:
[XmlRoot("LuckNumber")]
public class GenerateNumberResult
{
[XmlElement("Number")]
public string LuckNumber { get; set; }
[XmlElement("CodError")]
public string CodError{ get; set; }
[XmlElement("Serie")]
public string Serie { get; set; }
}
Can someone help me? Thanks!
The root of your XML is the "return" element. Add a wrapper class that contains your list:
[XmlRoot("return")]
public class ResultWrapper
{
[XmlElement("LuckNumber")]
public List<GenerateNumberResult> numberList;
}
And get the result:
ResultWrapper result = ProccessResult<ResultWrapper>(xml);
So i have a class, Texture2DProcessor, that inherits IXmlSerializable and Implicitly casts to and from Texture2D
public static implicit operator Texture2D(Texture2DProcessor o)
{
return o.Data;
}
public static implicit operator Texture2DProcessor(Texture2D o)
{
return o == null ? null : new Texture2DProcessor(o);
}
I then have a struct, GunProperties, that contains a Texture2D property with an XmlElement attribute with the type set to Texture2DProcessor
Texture2D _sideSprite;
[XmlElement(typeof(Texture2DProcessor))]
public Texture2D SideSprite
{
get { return _sideSprite; }
set { _sideSprite = value; }
}
I get the following runtime error
Cannot serialize member '...GunProperties.SideSprite' of type 'Microsoft.Xna.Framework.Graphics.Texture2D'
why is XmlSerializer not using Texture2DProcessor to read and write the Xml data?
Also I know that my ReadXml and WriteXml methods work because this works fine and i am able to use the texture.
Texture2D texture;
XmlSerializer serializer = new XmlSerializer(typeof(Texture2DProcessor));
serializer.Deserialize(new FileStream(path, FileMode.Open), texture);
The reason I'm going through this trouble is I'm using monogame and the content pipeline is pretty messed up especially for custom types and other than this issue i have it all working.
It looks like this may be a Mono XmlSerializer limitation. I have a small test app which works under .NET, but not under Mono 3.0.6.
But this looks fairly simple to work around:
[XmlIgnore]
public Texture2D SideSprite { get; set; }
[XmlElement("SideSprite")]
[EditorBrowsable(EditorBrowsableState.Never)]
public Texture2DProcessor SideSpriteProcessor
{
get { return SideSprite; }
set { SideSprite = value; }
}
We have the same issue in my Noda Time project, as XML serialization doesn't mix well with immutable types. I've given pretty much the same advice there.
EDIT: Okay, so here's some sample code which does work:
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
public class Person
{
public string Name { get; set; }
}
public class PersonWrapper : IXmlSerializable
{
public Person Person { get; set; }
public static implicit operator Person(PersonWrapper wrapper)
{
return wrapper == null ? null : wrapper.Person;
}
public static implicit operator PersonWrapper(Person person)
{
return new PersonWrapper { Person = person };
}
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
string name = reader.ReadString();
reader.ReadEndElement();
Person = new Person { Name = name };
}
public void WriteXml(XmlWriter writer)
{
writer.WriteString(Person.Name);
}
}
public class Company
{
[XmlElement(typeof(PersonWrapper))]
public Person Director { get; set; }
[XmlElement(typeof(PersonWrapper))]
public Person Manager { get; set; }
}
class Test
{
static void Main()
{
var serializer = new XmlSerializer(typeof(Company));
var original = new Company
{
Director = new Person { Name = "Holly" },
Manager = new Person { Name = "Jon" }
};
var writer = new StringWriter();
serializer.Serialize(writer, original);
Console.WriteLine("XML:");
Console.WriteLine(writer.ToString());
var reader = new StringReader(writer.ToString());
var deserialized = (Company) serializer.Deserialize(reader);
Console.WriteLine("Deserialized:");
Console.WriteLine("Director: {0}", deserialized.Director.Name);
Console.WriteLine("Manager: {0}", deserialized.Manager.Name);
}
}