Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Collection serialization

Status
Not open for further replies.

doseme

MIS
Nov 23, 2005
22
US
Have a collection to serialize.
No problems.

Problem:
How do you define the name of the objects in the collection?

Collection defined with <XmlRootAttribute> (OK)
Objects contained in the collection, I cannot define.

[Serializable(), XmlRoot("ASSETS")]
public class AssetCollection : DataObjectCollection
{
... collection implementation
}

[Serializable()]
public class Asset : DataObjectBase
{
[XmlElement("SERIAL_NUMBER")]
public string SerialNumber
{
...
}
... followed by other properties
}

Serializes to this:

<?xml version="1.0"?>
<ASSETS>
<Asset>
<SERIAL_NUMBER>2UA8110ZZ8</SERIAL_NUMBER>
... other nodes
</Asset>
</ASSETS>

Trying to define Asset as "TBL_ASSET"

Thank you for any assistance.
 
It looked like you had it all figured out.
You just need to set your attributes as different than the actual variable/class's name like you did with string SerialNumber, like this:

Code:
    [Serializable()]
    [XmlElement("TBL_ASSET")]
    public class Asset : DataObjectBase
    {
        [XmlElement("SERIAL_NUMBER")]
        public string SerialNumber
        {
        ...
        }
        ... followed by other properties
    }

That should work for you

Travis Hawkins
jobs.bestcodingpractices.com
 
The XmlElementAttribute can not be declared on a class.

Error: Attribute XmlElement is not valid on this declaration type. It is only valid on 'property, indexer, field, param, return' declarations.

(I have tried the element attribute on the indexer of the AssetCollection and it didn't work)

Thank you.

I don't mind manually serializing, I just hoped the 'tools' were already available.
 
Ah you're right, I knew I had just done this so I had to look it up... I hate that.

here's what you need to do:

Code:
    [Serializable()]
    [XmlRoot("TBL_ASSET")]
    public class Asset : DataObjectBase
    {
        [XmlElement("SERIAL_NUMBER")]
        public string SerialNumber
        {
        ...
        }
        ... followed by other properties
    }

sorry for the misdirection

Travis Hawkins
jobs.bestcodingpractices.com
 
The XmlRoot attribute only works on the root object.

If the Asset object is serialized by itself - ok.

The AssetCollection however; is the root object. So the XmlRoot attribute is ignored.
 
Hmm... I see you have a quandary, but I'm not quite sure what/where it is.

Where is the Asset class instatiated? If you need to instantiate multiple copies of the Asset class you can do this by putting them in an array of Assets and use:
[XmlElement("TBL_ASSET")] or [XmlArray("TBL_ASSET")] ,dependong in the behavior that you're after, on the instance.

That's another stab in the dark but I'm still not 100% sure that I understand your issue completely. Can you post where the Asset Class that you want to be serialized as "TBL_ASSET" is instantiated?


Travis Hawkins
jobs.bestcodingpractices.com
 
The AssetCollection is a collection of Asset objects.
 
I think this is what you've requested (And then some):

/// <summary>
/// Collection object for TBL_ASSET
/// </summary>
[Serializable(), XmlRoot("ASSETS")]
public class AssetCollection : DataObjectCollection
{
/// <summary>
/// Select command that will select all records from the TBL_ASSET with joined tables (TBL_ASSET_TYPE,TBL_MANUFACTURE)
/// </summary>
public override string SelectStatement()
{// temp

return "SELECT " +
// SerialNumber
"TBL_ASSET.SERIAL_NUMBER AS SERIAL_NUMBER, " +
// ModelNumber
"TBL_ASSET.MODEL_NUMBER AS MODEL_NUMBER, " +
// LessorAssetNumber
"TBL_ASSET.LESSOR_ASSET_NUMBER AS LESSOR_ASSET_NUMBER," +
// EquipDescription
"TBL_ASSET.EQUIP_DESCRIPTION AS EQUIP_DESCRIPTION," +
// AssetType
"TBL_ASSET.ASSET_TYPE AS ASSET_TYPE," +
// CostMonthly
"TBL_ASSET.COST_MONTHLY AS COST_MONTHLY, " +
// ManufactureId
"TBL_ASSET.MANUFACTURE_ID AS MANUFACTURE_ID," +
// ProductNumber
"TBL_ASSET.PRODUCT_NUMBER AS PRODUCT_NUMBER, " +
// AssetTypeDescription (AssetType)
"TBL_ASSET_TYPE.DESCRIPTION AS ASSET_TYPE_DESCRIPTION, " +
// ManufactureDescription (ManufactureId)
"TBL_MANUFACTURE.DESCRIPTION AS MANUFACTURE_DESCRIPTION " +

/* ANSI*/
"FROM LEASETRACK.TBL_ASSET INNER JOIN LEASETRACK.TBL_ASSET_TYPE ON TBL_ASSET_TYPE.ID = TBL_ASSET.ASSET_TYPE " +
"INNER JOIN LEASETRACK.TBL_MANUFACTURE on TBL_MANUFACTURE.ID = TBL_ASSET.MANUFACTURE_ID";


/*
"FROM LEASETRACK.TBL_ASSET, LEASETRACK.TBL_ASSET_TYPE, LEASETRACK.TBL_MANUFACTURE" +
"WHERE TBL_ASSET_TYPE.ID = TBL_ASSET.ASSET_TYPE AND TBL_MANUFACTURE.ID = TBL_ASSET.MANUFACTURE_ID;";
*/
}

/* Implement the required method Add */
public override int Add(System.Data.IDataReader reader)
{
while (reader.Read())
{
object[] values = new object[reader.FieldCount];

// copy
if (reader.GetValues(values) > 0)
{
// add Asset
List.Add(new Asset(values));
}
}

return List.Count;
}

... needed prior to manual serialization
public int Add(Asset asset)
{
return List.Add(asset);
}

public Asset this[int index]
{
get { return (Asset)List[index]; }
... setter removed (wasn't wanted anyway)
}

/* Default Serialization (didn't work)
public static void Serialize(System.IO.Stream stream, AssetCollection assets)
{
System.Xml.Serialization.XmlSerializer writer = new XmlSerializer(assets.GetType(),new Type[] { typeof(Asset) });
writer.Serialize(stream, assets);
}

public static AssetCollection Deserialize(System.IO.Stream stream)
{
System.Xml.Serialization.XmlSerializer reader = new XmlSerializer(typeof(AssetCollection),
new Type[] { typeof(Asset) });
return (AssetCollection)reader.Deserialize(stream);
}
*/

public static AssetCollection Deserialize(System.IO.Stream stream)
{
AssetCollection col = new AssetCollection();
System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();

xDoc.Load(stream);

System.Xml.XmlElement root = xDoc.DocumentElement;

foreach (System.Xml.XmlNode node in root.ChildNodes)
{
... load nodes
}

return col;
}

public static void Serialize(System.IO.Stream stream, AssetCollection assets)
{
// manual serialization
System.Xml.XmlDocument xDoc = assets.GetXML();

// write xml to stream
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(stream, null);

// formating (optional?)
writer.Formatting = System.Xml.Formatting.Indented;

xDoc.WriteContentTo(writer);

// Write to underlying stream
writer.Flush();
}

System.Xml.XmlDocument GetXML()
{
System.Xml.XmlDocument xDoc = new System.Xml.XmlDocument();

// simple declaration & Root - This is NOT a well formated document...
xDoc.LoadXml("<?xml version=\"1.0\"?><ASSETS></ASSETS>");

System.Xml.XmlElement root = xDoc.DocumentElement;

foreach (Asset item in this)
{
... Add nodes
}

return xDoc;
}
}

[Serializable()]
public class Asset : DataObjectBase, IDataAffecter
{
internal Asset(object[] values)
{
Load(values);
}

public Asset() { }

/// <summary>
/// Gets an update command based on the SerialNumber property.
/// </summary>
/// <returns>String to use as a CommandString</returns>
string IDataAffecter.GetUpdateStatement()
{
...
}

/// <summary>
/// Gets a delete command for the SerialNumber property.
/// </summary>
/// <returns>String to use as a CommandString</returns>
string IDataAffecter.GetDeleteStatement()
{
...
}

/// <summary>
/// Gets an insert command of the current properties.
/// </summary>
/// <returns>String to use as a CommandString</returns>
string IDataAffecter.GetInsertStatement()
{
...
}

/// <summary>
/// Gets a select command with additional filters of properties that have been set.
/// </summary>
/// <returns>String to use as a CommandString</returns>
/// <remarks>This function is only applicable for Asset objects that have not been created by an AssetCollection.</remarks>
public override string GetFilteredSelect()
{
...
}

#region fields
/* Fields have been given internal access to allow the AssetCollection to serialize the Asset object */

internal object _SerialNumber = null;
internal object _ModelNumber = null;
internal object _LessorAssetNumber = null;
internal object _EquipDescription = null;
internal object _AssetType = null;
internal object _CostMonthly = null;
internal object _ManufactureId = null;
internal object _ProductNumber = null;
internal object _AssetTypeDescription = null;
internal object _ManufactureDescription = null;
#endregion

public override void Load(object[] values)
{
int propertyCount = this.GetType().GetProperties().Length;

if (propertyCount != values.Length)
{
throw new ArgumentException("The values supplied do not match the expected array length of " +
propertyCount.ToString() + ".");
}

_SerialNumber = values[0];
_ModelNumber = values[1];
_LessorAssetNumber = values[2];
_EquipDescription = values[3];
_AssetType = values[4];
_CostMonthly = values[5];
_ManufactureId = values[6];
_ProductNumber = values[7];
_AssetTypeDescription = values[8];
_ManufactureDescription = values[9];
}

/// <summary>
/// TBL_ASSET.SERIAL_NUMBER
/// </summary>
[XmlElement("SERIAL_NUMBER")]
public string SerialNumber
{
get {
return (_SerialNumber == null) ? String.Empty : _SerialNumber.ToString();
}
set { _SerialNumber = value; }
}

/// <summary>
/// TBL_ASSET.MODEL_NUMBER
/// </summary>
[XmlElement("MODEL_NUMBER")]
public string ModelNumber
{
get {
return (_ModelNumber == null) ? String.Empty : _ModelNumber.ToString();
}
set { _ModelNumber = value; }
}

/// <summary>
/// TBL_ASSET.LESSOR_ASSET_NUMBER
/// </summary>
[XmlElement("LESSOR_ASSET_NUMBER")]
public string LessorAssetNumber
{
get {
return (_LessorAssetNumber == null) ? String.Empty : _LessorAssetNumber.ToString();
}
set { _LessorAssetNumber = value; }
}

/// <summary>
/// TBL_ASSET.EQUIP_DESCRIPTION
/// </summary>
[XmlElement("EQUIP_DESCRIPTION")]
public string EquipDescription
{
get {
return (_EquipDescription == null) ? String.Empty : _EquipDescription.ToString();
}
set { _EquipDescription = value; }
}

/// <summary>
/// TBL_ASSET.ASSET_TYPE
/// </summary>
[XmlElement("ASSET_TYPE")]
public int AssetType /* Double */
{
get {
return (_AssetType == null) ? new Int32() : Convert.ToInt32(_AssetType);
}
set { _AssetType = value; }
}

/// <summary>
/// TBL_ASSET.COST_MONTHLY
/// </summary>
[XmlElement("COST_MONTHLY")]
public Decimal CostMonthly
{
get {
return (_CostMonthly == null) ? new Decimal() : Convert.ToDecimal(_CostMonthly);
}
set { _CostMonthly = value; }
}

/// <summary>
/// TBL_ASSET.MANUFACTURE_ID
/// </summary>
[XmlElement("MANUFACTURE_ID")]
public int ManufactureId /* Double */
{
get {
return (_ManufactureId == null) ? new Int32() : Convert.ToInt32(_ManufactureId);
}
set { _ManufactureId = value; }
}

/// <summary>
/// TBL_ASSET.PRODUCT_NUMBER
/// </summary>
[XmlElement("PRODUCT_NUMBER")]
public string ProductNumber
{
get {
return (_ProductNumber == null) ? String.Empty : _ProductNumber.ToString();
}
set { _ProductNumber = value; }
}

/// <summary>
/// TBL_ASSET_TYPE.DESCRIPTION (ASSET_TYPE_DESCRIPTION)
/// </summary>
//[XmlElement("ASSET_TYPE_DESCRIPTION")]
public string AssetTypeDescription
{
get {
return (_AssetTypeDescription == null) ? String.Empty : _AssetTypeDescription.ToString();
}
//set { _AssetTypeDescription = value; }
}

/// <summary>
/// TBL_MANUFACTURE.DESCRIPTION (MANUFACTURE_DESCRIPTION)
/// </summary>
//[XmlElement("MANUFACTURE_DESCRIPTION")]
public string ManufactureDescription
{
get {
return (_ManufactureDescription == null) ? String.Empty : _ManufactureDescription.ToString();
}
//set { _ManufactureDescription = value; }
}
}

This is where the objects are used:

public class DataSource
{
IDbCommand cmd = null;
IDbConnection con = null;
IDataReader reader = null;

public DataSource(string connectionString)
{
// using Odbc
con = new OdbcConnection(connectionString);
}

public void Open()
{
con.Open();
}

public void Close()
{
con.Close();
}

#region Selection
public int Fill(DataObjectCollection objects)
{
cmd = con.CreateCommand();
cmd.CommandText = objects.SelectStatement();

reader = cmd.ExecuteReader();

objects.Add(reader);

reader.Close();
cmd.Dispose();

return objects.Count;
}

public int Fill(DataObjectCollection objects, string selectCommand)
{
...
}

public int Fill(DataObjectCollection objects, DataObjectBase selectFrom)
{
...
}
#endregion

#region Update
public int Update(IDataAffecter data)
{
...
}

public int BatchUpdate(DataObjectCollection objects)
{
...
}
#endregion

#region Insert

/// <summary>
/// Insert a IDataAffecter into a data source.
/// </summary>
/// <param name="data">IDataAffecter that contains the data to be inserted.</param>
/// <returns>Returns the number of records affected.</returns>
public int Insert(IDataAffecter data)
{
...
}

/// <summary>
/// Insert a collection of IDataAffecter items contained in a DataObjectCollection into a data source.
/// </summary>
/// <param name="objects">DataObjectCollection containing the data to be inserted.</param>
/// <returns>Returns the number of records affected.</returns>
public int BatchInsert(DataObjectCollection objects)
{
...
}
#endregion
}

Thank you for your time and assistance in this matter.
As you can see, I've moved to the manual serialization.
However; in this instance it worked well by allowing me to maintain some readonly properties for the Asset object- but there are several more instances where I could (and would prefer to) use the built in serialization - and these other instances are the same; a collection object and its contained objects.
 
hey,
I think I get the concept now. I'm trying to setup a stripped down version of that to verify but I think it would work to do this:

Code:
[XmlElement("TBL_ASSET")]
public Asset this[int index]
{
  get { return (Asset)List[index]; }
}

You may have already tried that?
That's what I was talking about before with setting the XmlElement on the instance rather than the class itself.

Let me know if that works, and I'll see if I can duplicate it.



Travis Hawkins
jobs.bestcodingpractices.com
 
Yes, I tried.
The error for applying the XmlElement to a class object stated that it was applicable to an indexer, but it didn't work.
 
I tested it out with a simple indexer and it worked so something else is hanging it up... I'm still working on it, just wanted you to know it should work. You might want to try it on one of your other classes to see if it works on any of the others, we might be able to track down the difference.

Travis Hawkins
jobs.bestcodingpractices.com
 
Actually, I tried something else.

Just a very simplified test (which didn't work)
If you don't mind, post your code that worked.

I'm sure it's something simple I'm not seeing...

I tried this (and several variations)

[Serializable(), XmlRoot("ThisRoot")]
public class Class3 : System.Collections.CollectionBase
{
[XmlElement("ThisNode")]
public Class2 this[int index]
{
get { return (Class2)List[index]; }
set { List[index] = value; }
}

public int Add(Class2 value)
{
return List.Add(value);
}

public void Serialize(System.IO.Stream stream)
{
System.Xml.Serialization.XmlSerializer writer = new XmlSerializer(typeof(Class3));
writer.Serialize(stream, this);
}
}

public class Class2
{
string val = String.Empty;

[XmlElement("ThisElement")]
public String Value
{
get { return val; }
set { val = value; }
}

public Class2() { }
public Class2(string value) { val = value; }
}

class Class1
{
static void Main(string[] args)
{
Class3 c = new Class3();
c.Add(new Class2("One"));
c.Add(new Class2("Two"));
c.Add(new Class2("Three"));
c.Add(new Class2("Four"));

System.IO.FileStream f = new System.IO.FileStream("test3.xml", System.IO.FileMode.Create);
Class3.Serialize(f, c);
f.Close();

return;
}
}

This is what I got:

<ThisRoot>
<Class2>
<ThisElement>One</ThisElement>
</Class2>
...
</ThisRoot>

Thank you again for your patience
 
ok,

I merged my test with yours and this is what I got:

Code:
[Serializable(), XmlRoot("ThisRoot"), XmlInclude(typeof(Class2))]
    public class Class3 : System.Collections.CollectionBase
    {
        [XmlElement("ThisNode")]
        public Class2 this[int index]
        {
            get { return (Class2)List[index]; }
            set { List[index] = value; }
        }

        public int Add(Class2 value)
        {
            return List.Add(value);
        }

        public void Serialize(System.IO.Stream stream)
        {
            System.Xml.Serialization.XmlSerializer writer = new XmlSerializer(typeof(Class3));
            writer.Serialize(stream, this);
        }
    }

    [XmlType("ThisElement")]
    public class Class2
    {
        string val = String.Empty;

        [XmlText]
        public String Value
        {
            get { return val; }
            set { val = value; }
        }

        public Class2() { }
        public Class2(string value) { val = value; }
    }

the output was:

Code:
<?xml version="1.0"?>
<ThisRoot xmlns:xsi="[URL unfurl="true"]http://www.w3.org/2001/XMLSchema-instance"[/URL] xmlns:xsd="[URL unfurl="true"]http://www.w3.org/2001/XMLSchema">[/URL]
  <ThisElement>One</ThisElement>
  <ThisElement>Two</ThisElement>
  <ThisElement>Three</ThisElement>
  <ThisElement>Four</ThisElement>
</ThisRoot>

I can't even remember if that's what we were going for to begin with :) are we close?

Travis Hawkins
jobs.bestcodingpractices.com
 
What I was going for was an output 'similar' to a DataSet

<DataSet> - Collection
<Table1> - Collection Objects
<Field1 /> - Object properties
<Field2 />
</Table1>
</DataSet>

And you provided the solution: XmlType

When I applied the XmlType (without the XmlElement on the indexer) it all came together.

Thank you, you were extreamly helpful.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top