sharplib/srl/srl.Xml.cs

132 lines
2.9 KiB
C#

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<XElement> _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. <Item type="Sword">)
// 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() );
}
}