Skip to content

Commit

Permalink
Better enable generic XML handling #571
Browse files Browse the repository at this point in the history
  • Loading branch information
Fraser Greenroyd committed Jun 21, 2023
1 parent f098b63 commit 995a32d
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 145 deletions.
1 change: 0 additions & 1 deletion XML_Adapter/CRUD/Create.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ protected override bool ICreate<T>(IEnumerable<T> objects, ActionConfig actionCo
BH.Engine.Base.Compute.RecordError("The Bluebeam markup schema is not supported for push operations at this time.");
return false;
default:
BH.Engine.Base.Compute.RecordNote("You have not supplied a supported XML Schema to push. Data is being pushed either using a schema set by XML Attributes on the objects, or a default schema based on object properties.");
return CreateDefault(objects, config);
}
}
Expand Down
53 changes: 13 additions & 40 deletions XML_Adapter/CRUD/Default/CreateDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,53 +58,26 @@ private bool CreateDefault<T>(IEnumerable<T> objects, XMLConfig config)
foreach (System.Reflection.PropertyInfo pi in bhomProperties)
overrides.Add(typeof(BHoMObject), pi.Name, new XmlAttributes { XmlIgnore = true });

StringWriter stringWriter = new StringWriter();

XmlSerializerNamespaces xns = new XmlSerializerNamespaces();
xns.Add("", "");
XmlSerializer szer = new XmlSerializer(typeof(T), overrides);

StreamWriter sw = new StreamWriter(_fileSettings.GetFullFileName());

List<T> obj = objects.ToList();

for(int x = 0; x < obj.Count; x++)
TextWriter ms = new StreamWriter(_fileSettings.GetFullFileName());
foreach (var obj in objects)
{
szer.Serialize(stringWriter, obj[x], xns);
string line = stringWriter.ToString();
if (x > 0)
line = line.Replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>\r\n", ""); //Don't need this header on every item
sw.WriteLine(line);

StringBuilder sb = stringWriter.GetStringBuilder();
sb.Remove(0, sb.Length);
try
{
szer.Serialize(ms, obj, xns);
}
catch(Exception e)
{
BH.Engine.Base.Compute.RecordError($"Error occurred when serialising object {obj.GetType()}. Error received was: {e.ToString()}.");
}
}

sw.Close();
ms.Close();
}
catch (Exception e)
{
//Try exporting the objects manually if automatic serialisation didn't work
try
{
List<string> xmlStrings = objects.Select(x => (x as object).ToXML()).ToList();

string xml = "<BHoM>";
xmlStrings.ForEach(x => xml += x);
xml += "</BHoM>";

XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(xml);
xdoc.Save(_fileSettings.GetFullFileName());
}
catch(Exception ex)
{
//Well something went terribly wrong so lets give the user both error messages to help with debugging
BH.Engine.Base.Compute.RecordError("An error occurred in serialising the objects of type " + objects.GetType() + ". Error messages will follow.");
BH.Engine.Base.Compute.RecordError(e.ToString());
BH.Engine.Base.Compute.RecordError(ex.ToString());
success = false;
}
BH.Engine.Base.Compute.RecordError($"Error serialising objects to XML. Error received: {e.ToString()}.");
success = false;
}

return success;
Expand Down
118 changes: 19 additions & 99 deletions XML_Adapter/CRUD/Default/ReadDefault.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,112 +51,32 @@ public partial class XMLAdapter : BHoMAdapter
{
private IEnumerable<IBHoMObject> ReadDefault(Type type = null, XMLConfig config = null)
{
List<string> docLines = File.ReadAllLines(_fileSettings.GetFullFileName()).ToList();

//Due to needing to read an XML file without a schema, we read each node instead and convert it to a JSON string which we then deserialise via Serialiser_Engine to get the custom objects
XmlTextReader reader = new XmlTextReader(new StringReader(string.Join(Environment.NewLine, docLines)));
XmlDocument doc = new XmlDocument();
XmlNode node = doc.ReadNode(reader);

if (node.NodeType == XmlNodeType.XmlDeclaration)
node = doc.ReadNode(reader); //Try the next node...

bool isBHoM = node.Name.ToLower() == "bhom";

string obj = ExtractData(node, null, isBHoM);

if (obj.EndsWith(","))
obj = obj.Substring(0, obj.Length - 1);

if(obj.StartsWith("\"BHoM\":{"))
object obj = null;
try
{
//An XML deserialised that was previously serialised as BHoM - remove the BHoM heading
obj = obj.Replace("\"BHoM\":{", "{");
TextReader reader = new StreamReader(_fileSettings.GetFullFileName());
XmlSerializer szer = new XmlSerializer(type);
obj = System.Convert.ChangeType(szer.Deserialize(reader), type);
reader.Close();
}
else
obj = "{" + obj + "}"; //Wrap the json string

return new List<CustomObject>() { BH.Engine.Serialiser.Convert.FromJson(obj) as CustomObject };
}

private string ExtractData(XmlNode node, string previousName = null, bool isBHoM = false)
{
//This is a recursive method
if (node == null || node.Name.ToLower().Contains("whitespace"))
return "";

string line = "";

string nodeName = node.Name.Replace("#", "");

if (previousName != nodeName)
catch(Exception e)
{
if (!isBHoM || (isBHoM && nodeName.ToLower() != "text"))
{
previousName = nodeName;
line += "\"" + previousName + "\":";
}
BH.Engine.Base.Compute.RecordError($"Error occurred while deserialising the XML to object of type {type}. Error received was: {e.ToString()}.");
return null;
}

if (node.HasChildNodes)
try
{
List<XmlNode> nodes1 = new List<XmlNode>();
foreach (XmlNode n in node.ChildNodes)
nodes1.Add(n);

var nodes = nodes1.GroupBy(x => x.Name.Replace("#", ""));

if(nodes.Any(x => x.Key.ToLower() != "text") || !isBHoM)
line += "{";

foreach (var group in nodes)
{
if (group.Key.ToLower().Contains("whitespace"))
continue;

if (group.Key.ToLower() == previousName.ToLower() && group.Count() == 1)
line += "\"" + previousName + "\":{";

if (group.Count() > 1)
{
line += "\"" + group.Key + "\":[";
previousName = group.Key;
}

foreach (var v in group)
{
string s = ExtractData(v, previousName, isBHoM);
if(!string.IsNullOrEmpty(s) && s != "\"\",")
line += s;
}

if (group.Key.ToLower() == previousName.ToLower() && group.Count() == 1)
{
if (line.EndsWith(","))
line = line.Substring(0, line.Length - 1); //Remove last ','
line += "},";
}

if (group.Count() > 1)
{
if (line.EndsWith(","))
line = line.Substring(0, line.Length - 1); //Remove last ','
line += "],";
}
}

if (line.EndsWith(","))
line = line.Substring(0, line.Length - 1); //Remove last ','

if (nodes.Any(x => x.Key.ToLower() != "text") || !isBHoM)
line += "}";
var bhomObj = (IBHoMObject)obj;
return new List<IBHoMObject> { bhomObj };
}
catch(Exception e)
{
BH.Engine.Base.Compute.RecordWarning($"Could not cast deserialised object back to an IBHoMObject. Data returned as CustomObject instead.");
CustomObject cObj = new CustomObject();
cObj.CustomData["Data"] = obj;
return new List<IBHoMObject> { cObj };
}
else if (node.Value != null)
line += "\"" + node.Value.Replace("\r", "").Replace("\n", "").Replace("\"", "'").Trim() + "\"";
else
line += "\"\""; //Empty element

return line + ",";
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions XML_Adapter/CRUD/Read.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.CodeDom;

using System.Reflection;

namespace BH.Adapter.XML
{
Expand All @@ -50,14 +53,14 @@ protected override IEnumerable<IBHoMObject> IRead(Type type, IList indices = nul
{
if (actionConfig == null)
{
BH.Engine.Base.Compute.RecordError("Please provide configuration settings to push to an XML file");
BH.Engine.Base.Compute.RecordError("Please provide configuration settings to pull from an XML file");
return new List<IBHoMObject>();
}

XMLConfig config = actionConfig as XMLConfig;
if (config == null)
{
BH.Engine.Base.Compute.RecordError("Please provide valid a XMLConfig object for pushing to an XML file");
BH.Engine.Base.Compute.RecordError("Please provide valid a XMLConfig object for pulling from an XML file");
return new List<IBHoMObject>();
}

Expand All @@ -70,13 +73,12 @@ protected override IEnumerable<IBHoMObject> IRead(Type type, IList indices = nul
case Schema.GBXML:
return ReadGBXML(type, config);
case Schema.KML:
BH.Engine.Base.Compute.RecordError("The KML Schema is not supported for pull operations at this time");
BH.Engine.Base.Compute.RecordError("The KML Schema is not supported for pull operations at this time.");
return new List<IBHoMObject>();
case Schema.Bluebeam:
return ReadBluebeam(type, config);
default:
BH.Engine.Base.Compute.RecordNote("You have not supplied a supported XML Schema to pull. Data is being returned as Custom Objects.");
return ReadDefault();
return ReadDefault(type, config);
}
}
}
Expand Down

0 comments on commit 995a32d

Please sign in to comment.