rj-action-library/Runtime/XML/XMLSerializer.cs

204 lines
4.9 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
namespace Rokojori
{
public class XMLSerializer
{
StringBuilder sb = new StringBuilder();
XMLWalker walker = new XMLWalker();
string indent = " ";
Dictionary<XMLNode, int> depthMap = new Dictionary<XMLNode, int>();
Dictionary<int, string> indentMap = new Dictionary<int, string>();
List<XMLElementNode> stack = new List<XMLElementNode>();
public static string Escape( string rawText )
{
rawText = RegexUtility.Replace( rawText, "&", "&amp;" );
rawText = RegexUtility.Replace( rawText, "<", "&lt;" );
rawText = RegexUtility.Replace( rawText, ">", "&gt;" );
rawText = RegexUtility.Replace( rawText, "\'", "&apos;" );
rawText = RegexUtility.Replace( rawText, "\"", "&quot;" );
return rawText;
}
public static string Unescape( string escapedText )
{
escapedText = RegexUtility.Replace( escapedText, "&amp;", "&" );
escapedText = RegexUtility.Replace( escapedText, "&lt;", "<" );
escapedText = RegexUtility.Replace( escapedText, "&gt;,", ">" );
escapedText = RegexUtility.Replace( escapedText, "&apos;", "\'" );
escapedText = RegexUtility.Replace( escapedText, "&quot;","\"" );
return escapedText;
}
public string SerializeXMLNode( XMLNode node )
{
walker.DepthIterate( node,
( n, d ) =>
{
var depth = GetDepth( n );
ClosePreviousElements( depth );
var element = n as XMLElementNode;
RJLog.Log( "Processing:", n );
if ( element != null )
{
sb.Append( "\n" );
sb.Append( GetIndent( depth ) );
sb.Append( "<" + element.fullNodeName );
for ( int i = 0; i < element.numAttributes; i++ )
{
var attribute = element.GetAttributeAt( i );
sb.Append( " " );
sb.Append( attribute.fullName );
sb.Append( "=\"" );
sb.Append( Escape( attribute.value ) );
sb.Append( "\"");
}
sb.Append( ">" );
stack.Add( element );
}
else
{
if ( n.parentElement != null && (
HTMLElementName.style.Selects( n.parentElement ) ||
HTMLElementName.script.Selects( n.parentElement ) )
)
{
sb.Append( n.nodeValue );
}
else
{
var nodeValue = n.nodeValue;
if ( nodeValue != null )
{
if ( XMLNode.NodeType.Text == n.nodeType )
{
sb.Append( Escape( n.nodeValue ) );
}
else
{
sb.Append( n.nodeValue );
}
}
}
}
},
false,
depthMap
);
ClosePreviousElements( -1 );
return sb.ToString();
}
public string SerializeHtml( XMLNode node )
{
sb.Append( "<!DOCTYPE html>" );
return SerializeXMLNode( node );
}
string GetIndent( int depth )
{
if ( indentMap.ContainsKey( depth ) )
{
return indentMap[ depth ];
}
if ( depth == 0 )
{
indentMap[ 0 ] = "";
return "";
}
if ( indentMap.ContainsKey( depth - 1 ) )
{
var smallerIndent = indentMap[ depth -1 ];
indentMap[ depth ] = smallerIndent + indent;
return indentMap[ depth ];
}
var sb = new StringBuilder();
for ( int i = 0; i < depth; i++ )
{
sb.Append( indent );
}
indentMap[ depth ] = sb.ToString();
return indentMap[ depth ];
}
int GetDepth( XMLNode n )
{
return walker.GetDepth( n, depthMap );
}
void ClosePreviousElements( int currentDepth )
{
for ( int i = stack.Count - 1; i >= 0; i-- )
{
var stackDepth = GetDepth( stack[ i ] );
if ( stackDepth >= currentDepth )
{
var element = stack[ i ];
stack.RemoveAt( i );
if ( NeedsLineBreak( element ) )
{
sb.Append( "\n" );
sb.Append( GetIndent( stackDepth ) );
}
sb.Append( "</" + element.fullNodeName + ">" );
}
else
{
i = -1;
}
}
}
bool NeedsLineBreak( XMLElementNode elementNode )
{
if ( elementNode.numChildren == 0 ||
elementNode.HasOnlyTextNodes() &&
! ( HTMLElementName.script.Selects( elementNode ) ||
HTMLElementName.style.Selects( elementNode )
)
)
{
return false;
}
return true;
}
}
}