Can you have of file of Type?
BlitzMax Forums/BlitzMax Beginners Area/Can you have of file of Type?
| ||
I remember using this in Pascal and it was very useful. I have searched, and seem to recall that this isn't possible? Pascal code which illustrates what I want to do: I know I can do it manually, step through the array of type, save either string, byte, double etc. but this means the code has to be modified every time I modify my type. |
| ||
You can use reflection and only write it once :) |
| ||
_JIM (Posted 3 minutes ago) #2 You can use reflection and only write it once :) Is there any example somewhere I look at? Even something very simple like the Pascal example I posted? |
| ||
This thread might help: http://www.blitzbasic.com/Community/posts.php?topic=76396#902424 Sadly, there's no complete documentation on how to do this. I'll try to dig up some of my old code. It used to parse every type that I chose in my editor and exported everything automagically as an XML. Loading was done the same way. I only needed to write them one :) |
| ||
Thanks. That would be great if you can find it :) |
| ||
Ah, found it!Type TBasicSerializable Method SaveToXML:TxmlNode(node:TxmlNode) Local tid:TTypeId = TTypeId.ForObject(Self) Local itemNode:TxmlNode = node.addChild(tid.name()) For Local fld:TField = EachIn tid.EnumFields() If (Lower(fld.MetaData("Serialize")) <> Lower("False")) If (fld.TypeId().name() = "TList") Local fldNode:TxmlNode = itemNode.addChild(fld.name()) For Local obj:TBasicSerializable = EachIn(TList(fld.Get(Self))) obj.SaveToXML(fldNode) Next Else Local strVal:String Select fld.TypeId() Case FloatTypeId, DoubleTypeId strVal = FloatToString(fld.GetFloat(Self)) Case IntTypeId, ByteTypeId, ShortTypeId, LongTypeId strVal = String(fld.GetInt(Self)) Case StringTypeId strVal = fld.GetString(Self) Default Local val:Object val = fld.Get(Self) If (val <> Null) Local fldNode:TxmlNode = itemNode.addChild(fld.Name()) Local obj:TBasicSerializable = TBasicSerializable(fld.Get(Self)) If (obj) obj.SaveToXML(fldNode) EndIf End Select If (strVal <> "") itemNode.addAttribute(fld.name(), strVal) EndIf End If EndIf Next Return itemNode End Method Method LoadFromXML(node:TxmlNode) Local tid:TTypeId = TTypeId.ForObject(Self) Local objNode:TxmlNode = node If (objNode = Null) Return End If For Local fld:TField = EachIn tid.EnumFields() If (Lower(fld.MetaData("Serialize")) <> Lower("False")) If (fld.TypeId().name() = "TList") Local fldList:TList = objNode.getChildren() For Local n:TxmlNode = EachIn fldList Local fieldName:String = fld.name() Local nodeName:String = n.getName() If (fieldName = nodeName) Local f:TList = New TList Local itemsList:TList = n.getChildren() If (itemsList <> Null) For Local itemNode:TxmlNode = EachIn itemslist Local nume:String = itemNode.getName() Local objType:TTypeId = TTypeId.ForName(nume) Local newObj:Object = objType.NewObject() objType.FindMethod("LoadFromXML").Invoke(newObj, [itemNode]) If (objType.MetaData("onLoad")) Local mLoaded:TMethod = objType.FindMethod(objType.MetaData("onLoad")) If (mLoaded) mLoaded.Invoke(newObj, Null) End If EndIf f.AddLast(newObj) Next EndIf fld.Set(Self, f) End If Next Else Select fld.TypeId() Case FloatTypeId fld.SetFloat(Self, Float(objNode.getAttribute(fld.name()))) Case DoubleTypeId fld.SetDouble(Self, Double(objNode.getAttribute(fld.name()))) Case IntTypeId, ByteTypeId, ShortTypeId, LongTypeId fld.SetInt(Self, Int(objNode.getAttribute(fld.name()))) Case StringTypeId fld.SetString(Self, objNode.getAttribute(fld.name())) Default Local chldNode:TXmlNode = FindChild(objNode, fld.Name()) If (chldNode) Local chld2Node:TXmlNode = FindChild(chldNode, fld.TypeId().Name()) Local new_obj:Object = fld.TypeId().NewObject() fld.TypeId().FindMethod("LoadFromXML").Invoke(new_obj, [chld2Node]) fld.Set(Self, new_obj) EndIf End Select End If Local tid:TTypeId = fld.TypeId() If (tid.MetaData("onLoad")) Local mLoaded:TMethod = tid.FindMethod(tid.MetaData("onLoad")) If (mLoaded) mLoaded.Invoke(Self, Null) End If EndIf EndIf Next End Method End Type It uses Brucey's tinyxml mod. IMHO this is too much of an overkill. I want to convert it to use MaXML mod instead, but got no time today. Now, I think it's pretty neat. All you have to do is extend it from your type: Type myType Extends TBasicSerializable Field TooSexy# {Serialize = "False"} Field Sexy% End Type Local myInstance:myType = New myType myInstance.TooSexy = 3.15 myInstance.Sexy = 2 'save Local pDoc:TxmlDoc = TxmlDoc.newDoc("1.0") Local root:TxmlNode = TxmlNode.newNode("Root") pDoc.setRootElement(root) myInstance.SaveToXML(root) pDoc.saveFormatFile("the_cake_is_a_lie.xml", True) 'load Local pDoc2 = TxmlDoc.parseFile("the_cake_is_a_lie.xml") Local root:TxmlNode = pDoc.getRootElement() myInstance.LoadFromXML(FindChild(root, "myType")) It's not perfect... could still be polished. But it works like a charm. It also handles TLists gracefully. However, it doesn't handle Arrays. Never needed them for this thing so far. Usually my arrays are internal-oly stuff, aka '{Serialize = "False"}' Hope this helps ;) Oh, you also have to use: Import brl.reflection Import bah.tinyxml Happy coding! EDIT: I forgot to add this (not that it matters much). Code is useable any way one wishes. Credit is nice, but entirely optional. Last edited 2011 |
| ||
Neat stuff. Just thought I'd pop in to remind you you can create a TList easily from an array (and vice versa) so it should be easy (though perhaps not truly elegant) to store arrays as well, just pump them through a list if you need to on the way to the file... |
| ||
Very nice. I will have a play with this code a bit later today and see if I can get my brain to comprehend it and use it in my program. Thanks very much _Jim. |
| ||
I just noticed that it needs these two functions:Function FloatToString:String(value:Float, places:Int = 3) Local sign:Int = Sgn(Value) value=Abs(value) Local i:Int = Round(Value * 10 ^ places) Local ipart:Int = Int(i / 10 ^ places) Local dpart:Int = i - ipart * 10 ^ places Local si:String = ipart Local di:String If dpart>0 di=dpart While di.length < places di="0"+di Wend di="."+di EndIf While Right(di,1)="0" di=Left(di,di.length-1) Wend If di="" di=".0" If sign=-1 si="-"+si Return si+di EndFunction Function Round:Float(val:Float) Local dec# dec#=val-Floor(val) If dec<0.5 Return Floor(val) Else Return Ceil(val) EndFunction I believe they're JoshK's work. They were in a PropertyGrid sample. |