132 lines
2.9 KiB
C#
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() );
|
|
}
|
|
}
|