using System; using System.Collections.Generic; using System.Xml.Linq; using System.Reflection; namespace srl.Xml; // --- INPUT ADAPTER --- public class XmlAdapter : IInput { private XElement _e; private XAttribute _a; public XmlAdapter( XElement e ) => _e = e; public XmlAdapter( XAttribute a ) => _a = a; public string? Value => _e?.Value ?? _a?.Value; public bool IsLeaf => _a != null || ( _e != null && !_e.HasElements ); public IInput? GetAttr( string name ) { if( _e == null ) return null; // Case-Insensitive search var a = _e.Attribute( name ) ?? _e.Attribute( name.ToLower() ); return a != null ? new XmlAdapter( a ) : null; } public IInput? GetChild( string name ) { if( _e == null ) return null; var c = _e.Element( name ) ?? _e.Element( name.ToLower() ); return c != null ? new XmlAdapter( c ) : null; } // Extra helper for Dot Notation scanning public IEnumerable<(string, string)> GetAllAttrs() { if( _e == null ) yield break; foreach( var a in _e.Attributes() ) yield return (a.Name.LocalName, a.Value); } } // --- OUTPUT DRIVER --- public class XmlDriver : Driver { private Stack _stack = new(); private XDocument _doc; public XDocument Document => _doc; public XmlDriver() { _doc = new XDocument(); } public void BeginScope( string name, Type type, int id ) { var el = new XElement( name ); // Polymorphism Metadata (if needed, e.g. ) // el.Add(new XAttribute("_type", type.Name)); if( id > 0 ) el.Add( new XAttribute( "_id", id ) ); // DAG ID if( _stack.Count > 0 ) _stack.Peek().Add( el ); else _doc.Add( el ); _stack.Push( el ); } public void EndScope() => _stack.Pop(); public void BeginCollection( string name, int count ) { // XML doesn't strictly need array wrappers, but it helps structure // We use the same BeginScope logic effectively var el = new XElement( name, new XAttribute( "_count", count ) ); if( _stack.Count > 0 ) _stack.Peek().Add( el ); _stack.Push( el ); } public void EndCollection() => _stack.Pop(); public void WriteAttr( string name, string value ) { if( _stack.Count == 0 ) return; _stack.Peek().Add( new XAttribute( name, value ) ); } public void WriteRef( string name, int id ) { var el = new XElement( name, new XAttribute( "_ref", id ) ); _stack.Peek().Add( el ); } public void OnProp( MemberInfo m, string name ) { /* Optional: Write tooltips/comments */ } } // --- FACADE --- public static class XmlSerializer { public static string Serialize( object obj ) { var driver = new XmlDriver(); srl.Walker.Serialize( obj, driver ); return driver.Document.ToString(); } public static void Deserialize( object root, string xml ) { var doc = XDocument.Parse( xml ); var adapter = new XmlAdapter( doc.Root ); // 1. Standard Load srl.Loader.Load( root, adapter ); // 2. Dot Notation Pass (MyBag Cool.Rare="Changed") srl.Loader.LoadDotNotations( root, adapter.GetAllAttrs() ); } }