I have a class that parses an XML document in C# using XElement.
I parse the XML for example:
IEnumerable<Element> elements =
from topLevelElement in XElement.Parse(xml).Elements("topLevel")
select new Element()
{
LongElement = Int64.Parse(topLevelElement.Element("long").Value),
StringElement = topLevelElement.Element("string").Value,
DateTimeElement = DateTime.Parse(topLevelElement.Element("datetime").Value)
};
What would be the best way to assert that the elements were properly parsed? I would like to check if LongElement, StringElement, and DateTimeElement is not null after parsing, but if there is a better way to go about this, I am open to it.
If you are unsure of the values that may be returned by the elements, you should really be using TryParse e.g.
int i = 0;
string s = "3";
if (Int32.TryParse(s, out i))
{
// Valid integer, now stored in i.
}
else
{
// Invalid integer.
}
Both your data types DateTime and Int32 have TryParse as an available method. As for a string, you can just do a trivial == null or String.IsNullOrEmpty
I would use functions from within Linq. These allow you to either throw an exception or set required defaults if you want your application to be not so strict ;)
Anyways, you get more control:
var elements = from topLevelElement in XElement.Parse(xml).Elements("topLevel")
select new Element()
{
LongElement = ConvertToInt(topLevelElement.Element("long").Value),
StringElement = topLevelElement.Element("string").Value,
DateTimeElement = DateTime.Parse(topLevelElement.Element("datetime").Value)
};
Where within ConvertToInt could do all you want, like:
public int ConvertToInt(object value)
{
if(value is int)
// return converted value
else
// return default, throw exception, etc
}
This is also a more reusable layout.
I would store the parse states in the element as a KeyValuePair:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var states = new string[] { "null", "empty", "noparse", "value" };
var xml = "<root>";
xml += "<topLevel><long>-13451245234</long><string>hello world</string><datetime>1/1/2012 8:00AM</datetime></topLevel>";
xml += "<topLevel><long>4563264643</long><string>lipsum</string><datetime></datetime></topLevel>";
xml += "<topLevel><string>hello world</string><datetime>1/1/2012 8:00AM</datetime></topLevel>";
xml += "</root>";
IEnumerable<Element> elements =
from topLevelElement in XElement.Parse(xml).Elements("topLevel")
select new Element
{
LongElement = ParseValue(topLevelElement, "long"),
DateTimeElement = ParseValue(topLevelElement, "datetime"),
StringElement = ParseValue(topLevelElement, "string"),
};
var idx = 0;
elements.All(e =>
{
Console.WriteLine("---- ELEMENT #{0} -----",idx++);
Console.WriteLine("[long] State: {0}\tValue:{1}\tType:{2}", states[e.LongElement.Key], e.LongElement.Value, (e.LongElement.Value).GetType());
Console.WriteLine("[datetime] State: {0}\tValue:{1}\tType:{2}", states[e.DateTimeElement.Key], e.DateTimeElement.Value, (e.DateTimeElement.Value).GetType());
Console.WriteLine("[string] State: {0}\tValue:{1}\tType:{2}", states[e.StringElement.Key], e.StringElement.Value, (e.StringElement.Value).GetType());
return true;
});
}
private static dynamic ParseValue(XElement parent, String propname)
{
var prop = parent.Element(propname);
dynamic val = null;
byte state = 255;
if (prop == null) state = 0;
else if (string.IsNullOrEmpty(prop.Value)) state = 1;
if (state < 255) return GetKVP(propname, state, GetDefaultValue(propname));
switch (propname)
{
case "string":
state = 3;
val = prop.Value;
break;
case "long":
Int64 longvalue;
if (Int64.TryParse(prop.Value, out longvalue)) { state = 3; val = longvalue; }
else state = 2;
break;
case "datetime":
DateTime datetimevalue;
if (DateTime.TryParse(prop.Value, out datetimevalue)) { state = 3; val = datetimevalue; }
else state = 2;
break;
default:
val = GetDefaultValue(propname);
break;
}
return GetKVP(propname,state,val);
}
private static dynamic GetKVP(string propname, byte state, object val)
{
if (propname == "long") return new KeyValuePair<byte, Int64>(state, (Int64)val);
if (propname == "datetime") return new KeyValuePair<byte, DateTime>(state, (DateTime)val);
if (propname == "string") return new KeyValuePair<byte, String>(state, (String)val);
return null;
}
private static dynamic GetDefaultValue(string propname)
{
if (propname == "long") return long.MinValue;
if (propname == "datetime") return DateTime.MinValue;
if (propname == "string") return null;
return null;
}
#region Nested type: Element
public struct Element
{
// States stored as byte, 0 = null, 1= empty, 2 = has a value
public KeyValuePair<byte,Int64> LongElement { get; set; }
public KeyValuePair<byte,String> StringElement { get; set; }
public KeyValuePair<byte,DateTime> DateTimeElement { get; set; }
}
#endregion
}
}
Related
I am hacking around with the Good For Nothing (GFN) compiler, trying to make it do a few different things. I am using the code from here: https://github.com/johandanforth/good-for-nothing-compiler
Regular GFN for loop:
var x = 0;
for x = 0 to 3 do
print x;
end;
This for loop always increments. I'd like to add decrement functionality:
var x = 0;
for x = 3 to 0 down //up for increment (works same as do)
print x;
end;
The main area I am struggling with is the CodeGen.
ForLoop class:
public class ForLoop : Stmt
{
public Stmt Body { get; set; }
public Expr From { get; set; }
public string Ident { get; set; }
public Expr To { get; set; }
public ArithOp Type { get; set; }
}
ArithOp enum:
public enum ArithOp
{
Add,
Sub,
Mul,
Div,
Up,
Down
}
Inside CodeGen.cs:
private void GenStmt(Stmt stmt)
{
//code omitted for brevity
else if (stmt is ForLoop)
{
// example:
// for x = 0 to 100 up
// "hello";
// end;
// x = 0
var forLoop = (ForLoop)stmt;
var assign = new Assign { Ident = forLoop.Ident, Expr = forLoop.From };
GenStmt(assign);
// jump to the test
var test = _il.DefineLabel();
_il.Emit(OpCodes.Br, test);
// statements in the body of the for loop
var body = _il.DefineLabel();
_il.MarkLabel(body);
GenStmt(forLoop.Body);
// to (increment/decrement the value of x)
_il.Emit(OpCodes.Ldloc, SymbolTable[forLoop.Ident]);
_il.Emit(OpCodes.Ldc_I4, 1);
_il.Emit(forLoop.Type == ArithOp.Up ? OpCodes.Add : OpCodes.Sub);
GenerateStoreFromStack(forLoop.Ident, typeof(int));
// **test** does x equal 100? (do the test)
_il.MarkLabel(test);
_il.Emit(OpCodes.Ldloc, SymbolTable[forLoop.Ident]);
GenerateLoadToStackForExpr(forLoop.To, typeof(int));
_il.Emit(OpCodes.Blt, body);
}
}
private void GenerateStoreFromStack(string name, Type type)
{
if (!SymbolTable.ContainsKey(name))
throw new Exception("undeclared variable '" + name + "'");
var locb = SymbolTable[name];
var localType = locb.LocalType;
if (localType != type)
throw new Exception(string.Format("'{0}' is of type {1} but attempted to store value of type {2}", name,
localType == null ? "<unknown>" : localType.Name, type.Name));
_il.Emit(OpCodes.Stloc, SymbolTable[name]);
}
private void GenerateLoadToStackForExpr(Expr expr, Type expectedType)
{
Type deliveredType;
if (expr is StringLiteral)
{
deliveredType = typeof(string);
_il.Emit(OpCodes.Ldstr, ((StringLiteral)expr).Value);
}
else if (expr is IntLiteral)
{
deliveredType = typeof(int);
_il.Emit(OpCodes.Ldc_I4, ((IntLiteral)expr).Value);
}
else if (expr is Variable)
{
var ident = ((Variable)expr).Ident;
deliveredType = expr.GetType();
if (!SymbolTable.ContainsKey(ident))
{
throw new Exception("undeclared variable '" + ident + "'");
}
_il.Emit(OpCodes.Ldloc, SymbolTable[ident]);
}
else if (expr is ArithExpr)
{
var arithExpr = (ArithExpr)expr;
var left = arithExpr.Left;
var right = arithExpr.Right;
deliveredType = expr.GetType();
GenerateLoadToStackForExpr(left, expectedType);
GenerateLoadToStackForExpr(right, expectedType);
switch (arithExpr.Op)
{
case ArithOp.Add:
_il.Emit(OpCodes.Add);
break;
case ArithOp.Sub:
_il.Emit(OpCodes.Sub);
break;
case ArithOp.Mul:
_il.Emit(OpCodes.Mul);
break;
case ArithOp.Div:
_il.Emit(OpCodes.Div);
break;
default:
throw new NotImplementedException("Don't know how to generate il load code for " + arithExpr.Op +
" yet!");
}
}
else
{
throw new Exception("don't know how to generate " + expr.GetType().Name);
}
if (deliveredType == expectedType) return;
if (deliveredType != typeof (int) || expectedType != typeof (string))
throw new Exception("can't coerce a " + deliveredType.Name + " to a " + expectedType.Name);
_il.Emit(OpCodes.Box, typeof (int));
_il.Emit(OpCodes.Callvirt, typeof (object).GetMethod("ToString"));
}
This currently generates an .exe that does nothing. Sources I have looked at to help solve this: http://www.codeproject.com/Articles/3778/Introduction-to-IL-Assembly-Language#Loop and https://ninjaferret.wordpress.com/2009/12/23/msil-4-for-loops/. I just don't know enough IL
Do this in C# code to get insight:
for (int ix = 0; ix < 3; ++ix) // up
for (int ix = 3; ix > 0; --ix) // down
There are two changes, you got the difference in the inc/dec operator. You didn't get the change in the loop termination condition. Which makes this the bug:
_il.Emit(OpCodes.Blt, body);
You'll have to invert that to Opcodes.Bgt
I have an XML like below :
<Decide Name="MemoryCheck" CommonUnit="MB">
<Decision CellColor="Red" Status="Critical" Exp="<=100" />
<Decision CellColor="Yellow" Status="Warning" Exp="<=200 & >100"/>
<Decision CellColor="Green" Status="OK" Exp=">200" />
</Decide>
For Input 50 MB, Output returned should be "Critical-Red"
For Input 142 MB, Output returned should be "Warning-Yellow"
For Input 212 MB, Output returned should be"OK-Green"
How to go about this using C# ??
Xml Name is "Decide.xml" and Code I have now :
XmlDocument xmldecide = new XmlDocument();
xmldecide.Load("C:\\Decide.xml");
XmlNodeList decidelist = xmldecide.GetElementsbyTagName("Decide");
XmlNode xdecide = decidelist[0];
string input = "50"; // Unit in MB
// Now I have to display the desired O/P "Critical-Red"
string input = "142"; // Unit in MB
// Now I have to display the desired O/P "Warning-Yellow"
string input = "212"; // Unit in MB
// Now I have to display the desired O/P "OK-Green"
Just a suggestion - If you have control of that xml you should consider creating a min and max attribute. Having to parse out conditional and integer information from a single attribute is ugly. That said, assuming you can't change the xml, here's a solution. It assumes the conditionals in the attribute are always in a similar format.
public static string AlertLevel(this XDocument decisionDocument, int size)
{
var queryResult = decisionDocument.Descendants("Decision");
foreach (var item in queryResult)
{
var expAttribute = item.Attribute("Exp");
if (expAttribute == null) continue;
var returnString = CreateResultString(item);
int minValue;
int maxValue;
if (expAttribute.Value.Contains(">") && expAttribute.Value.Contains("<="))
{
//evaluate minValue < size > maxValue
var stringValue = expAttribute.Value.Replace("<=", string.Empty).Replace(">", string.Empty).Trim();
var stringValueArray = stringValue.Split('&');
if (int.TryParse(stringValueArray[1], out minValue) &&
int.TryParse(stringValueArray[0], out maxValue))
{
if (minValue < size &&
size < maxValue)
return returnString;
}
}
else if (expAttribute.Value.Contains(">"))
{
//evaluate size > value
var stringValue = expAttribute.Value.Replace(">", string.Empty).Trim();
if (int.TryParse(stringValue, out maxValue))
{
if (size > maxValue)
return returnString;
}
}
else if (expAttribute.Value.Contains("<="))
{
//else evaluate size < value
var stringValue = expAttribute.Value.Replace("<=", string.Empty).Trim();
if (int.TryParse(stringValue, out minValue))
{
if (size < minValue)
return returnString;
}
}
}
return "No condition was met!";
}
private static string CreateResultString(XElement item)
{
var statusAttribute = item.Attribute("Status");
var returnString = statusAttribute == null ? "Status" : statusAttribute.Value;
var colorAttribute = item.Attribute("CellColor");
returnString += colorAttribute == null ? "-Color" : "-" + colorAttribute.Value;
return returnString;
}
usage
var xmlDecide = XDocument.Load("Decide.xml");
Console.WriteLine("50MB: " + xmlDecide.AlertLevel(50));
Console.WriteLine("142MB: " + xmlDecide.AlertLevel(142));
Console.WriteLine("212MB: " + xmlDecide.AlertLevel(212));
EDIT: You can use the same code for use with XmlDocument instead of XDocument. Just change "Attribute" to "Attributes.GetNamedItem" and "Descendants" to "GetElementsByTagName"
This is complicated.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string XML =
"<Decide Name=\"MemoryCheck\" CommonUnit=\"MB\">" +
"<Decision CellColor=\"Red\" Status=\"Critical\" Exp=\"<=100\" />" +
"<Decision CellColor=\"Yellow\" Status=\"Warning\" Exp=\"<=200 & >100\"/>" +
"<Decision CellColor=\"Green\" Status=\"OK\" Exp=\">200\" />" +
"</Decide>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(XML);
XmlNodeList memoryCheck = doc.GetElementsByTagName("Decision");
foreach(XmlNode decision in memoryCheck)
{
Decision newDecision = new Decision();
Decision.decisions.Add(newDecision);
newDecision.Cellcolor = decision.Attributes.GetNamedItem("CellColor").Value;
newDecision.status = decision.Attributes.GetNamedItem("Status").Value;
newDecision.low = 0;
newDecision.high = null;
string exps = decision.Attributes.GetNamedItem("Exp").Value;
string[] expsArray = exps.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string exp in expsArray)
{
if(exp.StartsWith("<="))
{
newDecision.high = int.Parse(exp.Substring(exp.IndexOf("=") + 1));
}
if(exp.StartsWith(">"))
{
newDecision.low = int.Parse(exp.Substring(exp.IndexOf(">") + 1));
}
}
}
Decision result = Decision.GetBySize(212);
}
}
public class Decision
{
public static List<Decision> decisions = new List<Decision>();
public string Cellcolor { get; set; }
public string status { get; set; }
public int? low { get; set; }
public int? high {get; set;}
public static Decision GetBySize(int memory)
{
Decision newDecision = null;
foreach(Decision decision in decisions)
{
if (memory >= decision.low)
{
if (decision.high == null)
{
newDecision = decision;
break;
}
else
{
if (memory <= decision.high)
{
newDecision = decision;
break;
}
}
}
}
return newDecision;
}
}
}
I have some XML I am receiving from a server that sometimes has some invalid characters that I would like to remove before deserialization. I have no control over the XML file I receive so I need to check for the invalid characters myself.
Sample XML.....
<PrintStatus>N</PrintStatus>
<CustomerPO> >>>> pearl <<<<< </CustomerPO>
<Description>PO# pearl</Description>
<BranchID>4</BranchID>
<PostDate>
<Date>01/13/2015</Date>
</PostDate>
<ShipDate>
<Date>01/13/2015</Date>
</ShipDate>
As you can see, the customer po section has the invalid characters I need to remove. This sometimes occurs only in certain elements that include user typed data.
Here is my Response code.....
//configure http request
HttpWebRequest httpRequest = WebRequest.Create(url) as HttpWebRequest;
httpRequest.Method = "POST";
//prepare correct encoding for XML serialization
UTF8Encoding encoding = new UTF8Encoding();
//use Xml property to obtain serialized XML data
//convert into bytes using encoding specified above and get length
byte[] bodyBytes = encoding.GetBytes(Xml);
httpRequest.ContentLength = bodyBytes.Length;
//get http request stream for putting XML data into
Stream httpRequestBodyStream = httpRequest.GetRequestStream();
//fill stream with serialized XML data
httpRequestBodyStream.Write(bodyBytes, 0, bodyBytes.Length);
httpRequestBodyStream.Close();
//get http response
HttpWebResponse httpResponse = httpRequest.GetResponse() as HttpWebResponse;
StreamReader httpResponseStream = new StreamReader(httpResponse.GetResponseStream(), System.Text.Encoding.ASCII);
//extract XML from response
string httpResponseBody = httpResponseStream.ReadToEnd();
httpResponseStream.Close();
//ignore everything that isn't XML by removing headers
httpResponseBody = httpResponseBody.Substring(httpResponseBody.IndexOf("<?xml"));
//deserialize XML into ProductInquiryResponse
XmlSerializer serializer = new XmlSerializer(typeof(MyResponseClass));
StringReader responseReader = new StringReader(httpResponseBody);
//return MyResponseClass result
return serializer.Deserialize(responseReader) as MyResponseClass;
Does anyone happen to have any suggestions to check the XML? Should I just check the elements I am concerned with right before the xml string gets deserialized? Or is there a better way?
A general fix for your problem would be to recursively descend the XML, parsing as you go and comparing to the schema for that node. At any point if the input differs from the input expected from the schema, or is malformed in some way, allow an error handler to run to fix the input stream, rolling back to the most recent good state and proceeding forward with the fixed input.
The .Net XmlTextReader class is not flexible enough to do this. However, if you know in advance that from the schema that certain XML Elements cannot have children, then the following will read an XML input stream, and upon encountering an element whose fully qualified name matches the known names of leaf nodes, and "escape" the text of all such nodes:
public enum XmlDoctorStatus
{
NoFixNeeded,
FixMade,
FixFailed
}
public class XmlDoctor
{
internal class XmlFixData
{
public string InitialXml { get; private set; }
public string FixedXml { get; private set; }
public int LineNumber { get; private set; }
public int LinePosition { get; private set; }
public XmlFixData(string initialXml, string fixedXml, int lineNumber, int linePosition)
{
this.InitialXml = initialXml;
this.FixedXml = fixedXml;
this.LineNumber = lineNumber;
this.LinePosition = linePosition;
}
public bool ComesAfter(XmlFixData other)
{
if (LineNumber > other.LineNumber)
return true;
if (LineNumber == other.LineNumber && LinePosition > other.LinePosition)
return true;
return false;
}
}
internal class XmlFixedException : Exception
{
public XmlFixData XmlFixData { get; private set; }
public XmlFixedException(XmlFixData data)
{
this.XmlFixData = data;
}
}
readonly HashSet<XName> childlessNodes;
public string OriginalXml { get; private set; }
public XmlDoctor(string xml, IEnumerable<XName> childlessNodes)
{
if (xml == null)
throw new ArgumentNullException();
this.OriginalXml = xml;
this.childlessNodes = new HashSet<XName>(childlessNodes);
}
List<int> indices = null;
string passXml = string.Empty;
bool inPass = false;
void InitializePass(string xml)
{
if (inPass)
throw new Exception("nested pass");
ClearElementData();
TextHelper.NormalizeLines(xml, out passXml, out indices);
inPass = true;
}
void EndPass()
{
inPass = false;
indices = null;
passXml = string.Empty;
ClearElementData();
}
static int LineNumber(XmlReader reader)
{
return ((IXmlLineInfo)reader).LineNumber;
}
static int LinePosition(XmlReader reader)
{
return ((IXmlLineInfo)reader).LinePosition;
}
// Taken from https://stackoverflow.com/questions/1132494/string-escape-into-xml
public static string XmlEscape(string escaped)
{
var replacements = new KeyValuePair<string, string>[]
{
new KeyValuePair<string,string>("&", "&"),
new KeyValuePair<string,string>("\"", """),
new KeyValuePair<string,string>("'", "'"),
new KeyValuePair<string,string>("<", "<"),
new KeyValuePair<string,string>(">", ">"),
};
foreach (var pair in replacements)
foreach (var index in escaped.IndexesOf(pair.Key, 0).Reverse())
if (!replacements.Any(other => string.Compare(other.Value, 0, escaped, index, other.Value.Length, StringComparison.Ordinal) == 0))
{
escaped = escaped.Substring(0, index) + pair.Value + escaped.Substring(index + 1, escaped.Length - index - 1);
}
return escaped;
}
void HandleNode(XmlReader reader)
{
// Adapted from http://blogs.msdn.com/b/mfussell/archive/2005/02/12/371546.aspx
if (reader == null)
{
throw new ArgumentNullException("reader");
}
switch (reader.NodeType)
{
case XmlNodeType.Element:
HandleStartElement(reader);
if (reader.IsEmptyElement)
{
HandleEndElement(reader);
}
break;
case XmlNodeType.Text:
HandleText(reader);
break;
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
break;
case XmlNodeType.CDATA:
break;
case XmlNodeType.EntityReference:
break;
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
break;
case XmlNodeType.DocumentType:
break;
case XmlNodeType.Comment:
break;
case XmlNodeType.EndElement:
HandleEndElement(reader);
break;
}
}
private void HandleText(XmlReader reader)
{
if (string.IsNullOrEmpty(currentElementLocalName) || string.IsNullOrEmpty(currentElementName))
return;
var name = XName.Get(currentElementLocalName, currentElementNameSpace);
if (!childlessNodes.Contains(name))
return;
var lineIndex = LineNumber(reader) - 1;
var charIndex = LinePosition(reader) - 1;
if (lineIndex < 0 || charIndex < 0)
return;
int startIndex = indices[lineIndex] + charIndex;
// Scan forward in the input string until we find either the beginning of a CDATA section or the end of this element.
// Patterns to match: </Name
//
string pattern1 = "</" + currentElementName;
var index1 = FindElementEnd(passXml, startIndex, pattern1);
if (index1 < 0)
return; // BAD XML.
string pattern2 = "<![CDATA[";
var index2 = passXml.IndexOf(pattern2, startIndex);
int endIndex = (index2 < 0 ? index1 : Math.Min(index1, index2));
var text = passXml.Substring(startIndex, endIndex - startIndex);
var escapeText = XmlEscape(text);
if (escapeText != text)
{
if (escapeText != XmlEscape(escapeText))
{
Debug.Assert(escapeText == XmlEscape(escapeText));
throw new InvalidOperationException("Escaping error");
}
string fixedXml = passXml.Substring(0, startIndex) + escapeText + passXml.Substring(endIndex, passXml.Length - endIndex);
throw new XmlFixedException(new XmlFixData(passXml, fixedXml, lineIndex + 1, charIndex + 1));
}
}
static bool IsXmlSpace(char ch)
{
// http://www.w3.org/TR/2000/REC-xml-20001006#NT-S
// [3] S ::= (#x20 | #x9 | #xD | #xA)+
return ch == '\u0020' || ch == '\u0009' || ch == '\u000D' || ch == '\u000A';
}
private static int FindElementEnd(string passXml, int charPos, string tagEnd)
{
while (true)
{
var index = passXml.IndexOf(tagEnd, charPos);
if (index < 0)
return index;
int endPos = index + tagEnd.Length;
if (index + tagEnd.Length >= passXml.Length)
return -1; // Bad xml?
// Now we must have zero or more white space characters and a ">"
while (endPos < passXml.Length && IsXmlSpace(passXml[endPos]))
endPos++;
if (endPos >= passXml.Length)
return -1; // BAD XML;
if (passXml[endPos] == '>')
return index;
index = endPos;
// Spurious ending, keep searching.
}
}
string currentElementName = string.Empty;
string currentElementNameSpace = string.Empty;
string currentElementLocalName = string.Empty;
private void HandleStartElement(XmlReader reader)
{
currentElementName = reader.Name;
currentElementLocalName = reader.LocalName;
currentElementNameSpace = reader.NamespaceURI;
}
private void HandleEndElement(XmlReader reader)
{
ClearElementData();
}
private void ClearElementData()
{
currentElementName = string.Empty;
currentElementNameSpace = string.Empty;
currentElementLocalName = string.Empty;
}
public XmlDoctorStatus TryFix(out string newXml)
{
XmlFixData data = null;
while (true)
{
XmlFixData newData;
var status = TryFixOnePass((data == null ? OriginalXml : data.FixedXml), out newData);
switch (status)
{
case XmlDoctorStatus.FixFailed:
Debug.WriteLine("Could not fix XML");
newXml = OriginalXml;
return XmlDoctorStatus.FixFailed;
case XmlDoctorStatus.FixMade:
if (data != null && !newData.ComesAfter(data))
{
Debug.WriteLine("Warning -- possible infinite loop detected, aborting fix");
newXml = OriginalXml;
return XmlDoctorStatus.FixFailed;
}
data = newData;
break; // Try to fix more
case XmlDoctorStatus.NoFixNeeded:
if (data == null)
{
newXml = OriginalXml;
return XmlDoctorStatus.NoFixNeeded;
}
else
{
newXml = data.FixedXml;
return XmlDoctorStatus.FixMade;
}
}
}
}
XmlDoctorStatus TryFixOnePass(string xml, out XmlFixData data)
{
try
{
InitializePass(xml);
using (var textReader = new StringReader(passXml))
using (XmlReader reader = XmlReader.Create(textReader))
{
while (true)
{
bool read = reader.Read();
if (!read)
break;
HandleNode(reader);
}
}
}
catch (XmlFixedException ex)
{
// Success - a fix was made.
data = ex.XmlFixData;
return XmlDoctorStatus.FixMade;
}
catch (Exception ex)
{
// Failure - the file was not fixed and could not be parsed.
Debug.WriteLine("Fix Failed: " + ex.ToString());
data = null;
return XmlDoctorStatus.FixFailed;
}
finally
{
EndPass();
}
// No fix needed.
data = null;
return XmlDoctorStatus.NoFixNeeded;
}
}
public static class TextHelper
{
public static void NormalizeLines(string text, out string newText, out List<int> lineIndices)
{
var sb = new StringBuilder();
var indices = new List<int>();
using (var sr = new StringReader(text))
{
string line;
while ((line = sr.ReadLine()) != null)
{
indices.Add(sb.Length);
sb.AppendLine(line);
}
}
lineIndices = indices;
newText = sb.ToString();
}
public static IEnumerable<int> IndexesOf(this string str, string value, int startAt)
{
if (str == null)
yield break;
for (int index = startAt, valueLength = value.Length; ; index += valueLength)
{
index = str.IndexOf(value, index);
if (index == -1)
break;
yield return index;
}
}
}
Then use it like:
public static class TestXmlDoctor
{
public static void TestFix()
{
string xml1 = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<MainClass>
<PrintStatus>N</PrintStatus>
<CustomerPO> >>>> pearl <<<<< </CustomerPO>
<Description>PO# pearl</Description>
<BranchID>4</BranchID>
<PostDate>
<Date>01/13/2015</Date>
</PostDate>
<ShipDate>
<Date>01/13/2015</Date>
</ShipDate>
</MainClass>
";
XName[] childlessNodes1 = new XName[]
{
XName.Get("CustomerPO", string.Empty),
};
try
{
TestFix(xml1, childlessNodes1);
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
public static string TestFix(string xml, IEnumerable<XName> childlessNodes)
{
string fixedXml;
var status = (new XmlDoctor(xml, childlessNodes).TryFix(out fixedXml));
switch (status)
{
case XmlDoctorStatus.NoFixNeeded:
return xml;
case XmlDoctorStatus.FixFailed:
Debug.WriteLine("Failed to fix xml");
return xml;
case XmlDoctorStatus.FixMade:
Debug.WriteLine("Fixed XML, new XML is as follows:");
Debug.WriteLine(fixedXml);
Debug.WriteLine(string.Empty);
return fixedXml;
default:
Debug.Assert(false, "Unknown fix status " + status.ToString());
return xml;
}
}
}
This with this, your XML fragment can be parsed, and becomes:
<?xml version="1.0" encoding="UTF-8"?>
<MainClass>
<PrintStatus>N</PrintStatus>
<CustomerPO> >>>> pearl <<<<< </CustomerPO>
<Description>PO# pearl</Description>
<BranchID>4</BranchID>
<PostDate>
<Date>01/13/2015</Date>
</PostDate>
<ShipDate>
<Date>01/13/2015</Date>
</ShipDate>
</MainClass>
I have a method as shown below.
public IResults GetResults(
string code = "",
string id = "",
DateTime? registerDate = null,
IEnumerable<Category> type = null,
int pageNum = 1,
bool all = false)
{
....
........
}
For every non-null or non-empty parameter I would like to have the parameter name and value to appended to the URI string.
For example, if the parameter code is a non-empty string then I would want it to be appended to the standard URI result/? as results/?code=passedcodevalue. If it is empty then there would be no need to append it.
Similarly, these are the rules to construct the URI:
when code or id is not empty append it to - results/? - as
results/?code=abc&id=xyz
when registerDate is not null append it to - results/? - as
results/?code=abc&id=xyz®isterdate=2004-06-29
when type is not null append it to - results/? - as
results/?code=abc&id=xyz®isterdate=2004-06-29&type=qwerty
when pagenum is not 1 append it to - results/? - as
results/?code=abc&id=xyz®isterdate=2004-06-29&type=qwerty&pagenum=5
when all is not false append it to - results/? - as
results/?code=abc&id=xyz®isterdate=2004-06-29&type=qwerty&pagenum=5&all=true
What is the optimal and generic way to do this?
Try this:
public string GetResults(
string code = "",
string id = "",
DateTime? registerDate = null,
IEnumerable<Category> type = null,
int pageNum = 1,
bool all = false)
{
var result = "results/?";
var firstFound = false; //Used for add & to string
for (int i = 0; i < 6; i++)
{
var partial = string.Empty;
switch (i)
{
case 0:
partial = GetIfNotEmpty("code", code);
break;
case 1:
partial = GetIfNotEmpty("id", id);
break;
case 2:
partial = GetIfNotEmpty("registerDate", registerDate);
break;
case 3:
partial = GetIfNotEmpty("type", type);
break;
case 4:
partial = GetIfNotEmpty("pageNum", pageNum);
break;
default:
partial = GetIfNotEmpty("all", all);
break;
}
if (string.IsNullOrWhiteSpace(partial))
continue;
if (firstFound)
result += "&";
else
firstFound = true;
result += partial;
}
return result;
}
private string GetIfNotEmpty<T>(string name, T value)
{
if (value == null)
return string.Empty;
if (value is IEnumerable)
{
var enumerableFinalString = string.Empty;
foreach (var item in ((IEnumerable)value))
{
enumerableFinalString += item.ToString();
}
return string.Format("{0}={1}", name, enumerableFinalString);
}
return string.Format("{0}={1}", name, value.ToString());
}
I can loop through all the fields in a PDF using ABCpdf using the GetFieldNames() collection and get their properties but the one I can't seem to get is whether or not the field is multi-line text field or not. Is there any example out there of how to find this property? My code is below if it's helpful but it's probably unnecessary.
....
foreach (string fieldName in doc.Form.GetFieldNames())
{
WebSupergoo.ABCpdf9.Objects.Field f = doc.Form[fieldName];
dt = GetFieldInstances(dt,f);
}
....
private static DocumentTemplate GetFieldInstances(DocumentTemplate dt, WebSupergoo.ABCpdf9.Objects.Field f)
{
Field field;
Instance inst = new Instance();
int instanceCount = 0;
bool fieldAlreadyExists = dt.Fields.Any(currentField => currentField.Name == f.Name);
if (!fieldAlreadyExists)
{
field = new Field();
field.Name = f.Name;
field.Value = f.Value;
field.Format = f.Format == null ? null : f.Format;
field.PartialName = f.PartialName;
field.TypeID = (int)f.FieldType;
//field.IsMultiline =
//field.IsRequired =
}
else
{
field = (from currentField in dt.Fields where currentField.Name == f.Name select currentField).SingleOrDefault();
instanceCount = field.Instances.Count();
}
if ((Field.FieldTypes)f.FieldType == Field.FieldTypes.Radio || (Field.FieldTypes)f.FieldType == Field.FieldTypes.Checkbox)
{
inst.ExportValue = f.Options[instanceCount];
}
if (f.Kids.Count() > 0)
{
f = f.Kids[instanceCount];
}
inst.Bottom = (int)f.Rect.Bottom;
inst.Height = (int)f.Rect.Height;
inst.Left = (int)f.Rect.Left;
inst.Width = (int)f.Rect.Width;
inst.PageNumber = f.Page.PageNumber;
field.Instances.Add(inst);
if (!fieldAlreadyExists)
{
dt.Fields.Add(field);
}
return dt;
}
I figured it out:
public bool GetIsMultiLine(WebSupergoo.ABCpdf9.Objects.Field field)
{
var flags = Atom.GetInt(Atom.GetItem(field.Atom, "Ff"));
var isMultiLine = GetIsBitFlagSet(flags, 13);
return isMultiLine;
}
public bool GetIsRequired(WebSupergoo.ABCpdf9.Objects.Field field)
{
var flags = Atom.GetInt(Atom.GetItem(field.Atom, "Ff"));
var isRequired = GetIsBitFlagSet(flags, 2);
return isRequired;
}
public bool GetIsReadOnly(WebSupergoo.ABCpdf9.Objects.Field field)
{
var flags = Atom.GetInt(Atom.GetItem(field.Atom, "Ff"));
var isReadOnly = GetIsBitFlagSet(flags, 1);
return isReadOnly;
}
private static bool GetIsBitFlagSet(int b, int pos)
{
return (b & (1 << (pos - 1))) != 0;
}
In case your not familiar with unsigned integer / binary conversions, I found this site really helpful to understand.
For example, let's say that the integer returned for a field's flags equals 4096. If you enter that into the online coversion tool in that website, it will show you that the 13th bit position is turned on (1 instead of a 0 in the 13th position starting from the right).
In the ABCPdf guide, it says that Multi-Line is bit position 13 so you know that field is a Multi-Line field.
Likewise for the 2nd position for Required and the 1st position for Read-only.