Can anyone help me. I'm trying to modify the yaml config.
This is the sample yaml:
person:
# some comments
name: "Test"
# some comments
age: 20
I want to modify only the age without affecting like comments and other infos.
And then save it again to file.
But in my trial, the comments is not being save but only the deserialized data.
Thank you.
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using var reader = new StreamReader("test.yaml");
var scanner = new Scanner(reader, skipComments: false);
var parser = new Parser(scanner);
//using var writer = new StreamWriter("test2.yaml");
//var emitter = new Emitter(writer);
var emitter = new Emitter(Console.Out);
while (parser.MoveNext())
{
if (parser.Current is Scalar scalar)
{
if (scalar.IsKey && scalar.Value == "age")
{
emitter.Emit(scalar);
parser.MoveNext(); // move to age value
emitter.Emit(new Scalar("30")); // new age value
continue;
}
}
emitter.Emit(parser.Current);
}
It almost works. The following result is obtained:
person
# some comments
:
name: "Test"
# some comments
age: 30
The emitter violates the output. A colon is placed after the comment.
Do further research yourself.
Related
I'm following a tutorial which has given me this sample code. I want to alter it so the user inputs the data through the console rather than in the code but I'm unsure how to, any ideas?
using MyMLApp;
// Add input data
var sampleData = new SentimentModel.ModelInput()
{
Col0 = "Will never go here again!"
};
// Load model and predict output of sample data
var result = SentimentModel.Predict(sampleData);
// If Prediction is 1, sentiment is "Positive"; otherwise, sentiment is "Negative"
string sentiment = result.Prediction == 1 ? "Positive" : "Negative";
Console.WriteLine($"Text: {sampleData.Col0}\nSentiment: {sentiment}");
Use Console.ReadLine(). Optionally handle empty input in your code.
string? input = Console.ReadLine();
var sampleData = new SentimentModel.ModelInput()
{
Col0 = input
};
Does anyone know how to convert the below nested JSON to CSV via CHOETL (An ETL framework for .NET)? Thank you!
I'm using this code but it will only return the first equipment record.
CODE:
{
using (var json = new ChoJSONReader("./test.json"))
{
csv.Write(json.Cast<dynamic>().Select(i => new
{
EquipmentId = i.GpsLocation.Equipment[0].EquipmentId,
InquiryValue = i.GpsLocation.Equipment[0].InquiryValue,
Timestamp = i.GpsLocation.Equipment[0].Timestamp
}));
}
}
JSON:
"GpsLocation": {
"Equipment": [
{
"EquipmentId": "EQ00001",
"InquiryValue": [
"IV00001"
],
"Timestamp": "2020-01-01 01:01:01.01",
},
{
"EquipmentId": "EQ00002",
"InquiryValue": [
"IV00002"
],
"Timestamp": "2020-01-01 01:01:01.01"
}
]
}
}````
As others suggest, the issue is you are only looking at the first element of the array.
It appears that the easiest way to control what you serialise into CSV is by correctly defining your source objects from JSON. JSON Path expressions come in pretty handy.
What I ended up doing here is query all JSON to return an array of Equipment objects regardless of where they are in the hierarchy (which means you may need to filter it a bit better depending on your full JSON).
Then it's pretty easy to define each field based on JSON path and just pass the result to CSVWriter.
Also check out some gotchas that I outlined in the respective comment lines.
void Main()
{
var jsonString = "{\"GpsLocation\":{\"Equipment\":[{\"EquipmentId\":\"EQ00001\",\"InquiryValue\":[\"IV00001\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"},{\"EquipmentId\":\"EQ00002\",\"InquiryValue\":[\"IV00002\"],\"Timestamp\":\"2020-01-01 01:01:01.01\"}]}}";
var jsonReader = new StringReader(jsonString);
var csvWriter = new StringWriter(); // outputs to string, comment out if you want file output
//var csvWriter = new StreamWriter(".\\your_output.csv"); // writes to a file of your choice
using (var csv = new ChoCSVWriter(csvWriter))
using (var json = new ChoJSONReader(jsonReader)
.WithJSONPath("$..Equipment[*]", true) // firstly you scope the reader to all Equipment objects. take note of the second parameter. Apparently you need to pass true here as otherwise it just won't return anythig
.WithField("EquipmentId", jsonPath: "$.EquipmentId", isArray: false) // then you scope each field in the array to what you want it to be. Since you want scalar values, pass `isArray: false` for better predictability
.WithField("InquiryValue", jsonPath: "$.InquiryValue[0]", isArray: false) // since your InquiryValue is actually an array, you want to obtain first element here. if you don't do this, fields names and values would go askew
.WithField("Timestamp", jsonPath: "$.Timestamp", fieldType: typeof(DateTime), isArray: false)) // you can also supply field type, otherwise it seems to default to `string`
{
csv.WithFirstLineHeader().Write(json);
}
Console.WriteLine(csvWriter.GetStringBuilder().ToString()); // comment this out if writing to file - you won't need it
}
Update summary:
Pivoted to update the code to rely on JSON Path scoping - this seems to allow for field name manipulation with pretty low effort
Looking at your comment, you could probably simplify your file writer a little bit - use StreamWriter instead of StringWriter - see updated code for example
Here is the working sample of producing CSV from your JSON
string json = #"{
""GpsLocation"": {
""Equipment"": [
{
""EquipmentId"": ""EQ00001"",
""InquiryValue"": [
""IV00001""
],
""Timestamp"": ""2020-02-01 01:01:01.01"",
},
{
""EquipmentId"": ""EQ00002"",
""InquiryValue"": [
""IV00002""
],
""Timestamp"": ""2020-01-01 01:01:01.01""
}
]
}
}";
StringBuilder csv = new StringBuilder();
using (var r = ChoJSONReader.LoadText(json)
.WithJSONPath("$.GpsLocation.Equipment")
.WithField("EquipmentId")
.WithField("InquiryValue", jsonPath: "InquiryValue[0]", fieldType: typeof(string))
.WithField("Timestamp", fieldType: typeof(DateTime))
)
{
using (var w = new ChoCSVWriter(csv)
.WithFirstLineHeader())
w.Write(r);
}
Console.WriteLine(csv.ToString());
Output:
EquipmentId,InquiryValue,Timestamp
EQ00001,IV00001,2/1/2020 1:01:01 AM
EQ00002,IV00002,1/1/2020 1:01:01 AM
Sample fiddle: https://dotnetfiddle.net/hJWtqH
Your code is sound, but the issue is that you're only writing the first variable in the array by using i.GpsLocation.Equipment[0]. Instead, try looping over everything by putting it into a for loop, and changing the [0] to your iterating variable inside of said loop.
Using the example code from the Unity Developer Guide | Parse
# https://www.parse.com/docs/unity_guide#objects-updating
// Create the object.
var gameScore = new ParseObject("GameScore")
{
{ "score", 1337 },
{ "playerName", "Sean Plott" },
{ "cheatMode", false },
{ "skills", new List<string> { "pwnage", "flying" } },
};
gameScore.SaveAsync().ContinueWith(t =>
{
// Now let's update it with some new data. In this case, only cheatMode
// and score will get sent to the cloud. playerName hasn't changed.
gameScore["cheatMode"] = true;
It just adds a new row and leaves the original row unchanged.
I guess i'm thinking Parse would do something "SQL like" such as UPDATE where primaryKey = 123.
Searching for an answer i found this code #
https://parse.com/questions/updating-a-field-without-retrieving-the-object-first, but there was no example in C#. All attempts to port this to C# result in multiple syntax errors.
UnityScript:
// Create a pointer to an object of class Point with id dlkj83d
var Point = Parse.Object.extend("Point");
var point = new Point();
point.id = "dlkj83d";
// Set a new value on quantity
point.set("quantity", 6);
// Save
point.save(null, {
success: function(point) {
// Saved successfully.
},
error: function(point, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
}
});
Does Parse have some way to update a row that already exists using C#? And where is it in the docs? And how can their own example be so useless?
One of the posts related to my question stated "retrieve the object, then write it back with the changes" and i had not the faintest idea how to execute the stated objective (especially after the epic fail of Parse Documentation's example code)
Here is what i have been able to figure out and make work:
var query = new ParseQuery<ParseObject>("Tokens")
.WhereEqualTo ("objectId", "XC18riofu9");
query.FindAsync().ContinueWith(t =>
{
var tokens = t.Result;
IEnumerator<ParseObject> enumerator = tokens.GetEnumerator();
enumerator.MoveNext();
var token = enumerator.Current;
token["power"] = 20;
return token.SaveAsync();
}).Unwrap().ContinueWith(t =>
{
// Everything is done!
//Debug.Log("Token has been updated!");
});
the first part retrieves the object with the stated objectId, the second part sets the fields in the object. The third part reports all is well with the operation.
it's a monkey see, monkey do understanding at this point being that i do not understand the finer points in the code.
the code can be tested by creating a class named "Tokens". in that class create a tokenName field and a power field. make a few rows with Fire, water, mud as the tokenNames. Replace the objectId in the .WhereEqualTo clause with a valid objectId or any other search parameters you like. Execute the code and observe the changes in the Parse Data Browser.
For extra credit create the class required to implement the example code from the Chaining Tasks Together section of Parse's Documentation.
https://www.parse.com/docs/unity_guide#tasks-chaining
I probably titled this incorrectly, but I have a file with multiple lines structured like so:
#### ID: 0 NAME: card_inventory ####
{ ALL CONTENT WOULD GO BETWEEN HERE }
#### ENDCARD ####
#### ID: 1 NAME: card_inventory ####
{ ALL CONTENT WOULD GO BETWEEN HERE }
{ I WANT TO REMOVE ALL REFERENCES TO
THIS CARD WITH AN ID OF 1 }
#### ENDCARD ####
So basically my question is how can I located the card id and delete it's contents? I was thinking maybe RegEX, but I'm not sure as I've never attempted this sort of thing before.
==== UPDATE ====
So I sort of have this working, but I've got a new problem, when I call my delete action
using (StreamWriter sw = File.AppendText(this.cardinfo_path))
{
sw.WriteLine(Regex.Replace(this.cardinfo_path,
#"####\sID:\s(" + id + #")\s.*?####\sENDCARD\s####", "TACO",
RegexOptions.Singleline));
}
It simply add the file name to the end of the file. Where am I going wrong? I feel like such a newbie asking this question.
Regex may not be the ideal solution, but if your data is structured that cleanly then it will work
####\sID:\s(1)\s.*?####\sENDCARD\s####
See it at http://refiddle.com/1n0
And just replace (1) with the ID you want to remove.
Regex.Replace( filecontents,
#"####\sID:\s(" + cardid + #")\s.*?####\sENDCARD\s####",
"",
RegexOptions.Singeline );
The key to this regular expression is using the non-greedy .*? match so that it will match the first occurrence of ENDCARD following the id.
I would do this with one loop reading the file, a boolean flag triggered on detection of "#### ID: 1" (and reset on next detection of "####"), and another stream writing to the output file if the boolean flag is true.
The code would look approximately like (hand-written):
var reader = new FileReader ("input.cards");
var writer = new FileWriter ("filtered.cards");
var writeToOutput = true;
while (! reader.EndOfStream)
{
var inputLine = reader.ReadLine ();
if (inputLine.StartsWith ("####"))
{
// control line
if (inputLine.Contains ("ID: 1"))
writeToOutput = false;
if (! writeToOutput && inputLine.Contains ("ENDCARD"))
writeToOutput = true;
}
if (writeToOutput)
writer.WriteLine (inputLine);
}
May it can be done with a single regex but a little filter should do it as well (also gives you a bit more flexibility in case you want to do something more):
bool skipCurrentCart = false;
var cartFileName = "yourcartfile";
var tmpFileName = Path.GetTempFileName();
using (var reader = new StreamReader(cartFileName))
using (var writer = new StreamWriter(tmpFileName))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (!skipCurrentCart)
{
writer.WriteLine(line);
}
if (line.StartsWith(string.Format("#### ID: {0} NAME: card_inventory ####", cartIdToIgnore)))
{
skipCurrentCart = true;
}
else if (line.StartsWith("#### ENDCARD ####"))
{
skipCurrentCart = false;
}
}
}
// replace cart file
File.Move(tmpFileName, cartFileName);
I'm confused by your question. You seem to be wanting to different things. Do you want to remove the card or just its contents. The following will only remove its contents.
(?<=(#### ID: 1 NAME: card_inventory ####))(.|\n)+?(?=(#### ENDCARD ####))
Just change the card ID to whatever you need.
Perhaps:
"(?<=(#### ID: " + cardId + " NAME: card_inventory ####))(.|\n)+?(?=(#### ENDCARD ####))"
This will only get the data in between excluding the #### stuff.
What I have
I have templates that are stored in a database, and JSON data that gets converted into a dictionary in C#.
Example:
Template: "Hi {FirstName}"
Data: "{FirstName: 'Jack'}"
This works easily with one level of data by using a regular expression to pull out anything within {} in the template.
What I want
I would like to be able to go deeper in the JSON than the first layer.
Example:
Template: "Hi {Name: {First}}"
Data: "{Name: {First: 'Jack', Last: 'Smith'}}"
What approach should I be taking? (and some guidance on where to start with your pick)
A regular expression
Not use JSON in the template (in favor of xslt or something similar)
Something else
I'd also like to be able to loop through data in the template, but I have no idea at all where to start with that one!
Thanks heaps
You are in luck! SmartFormat does exactly as you describe. It is a lightweight, open-source string formatting utility.
It supports named placeholders:
var template = " {Name:{Last}, {First}} ";
var data = new { Name = new { First="Dwight", Last="Schrute" } };
var result = Smart.Format(template, data);
// Outputs: " Schrute, Dwight " SURPRISE!
It also supports list formatting:
var template = " {People:{}|, |, and} ";
var data = new { People = new[]{ "Dwight", "Michael", "Jim", "Pam" } };
var result = Smart.Format(template, data);
// Outputs: " Dwight, Michael, Jim, and Pam "
You can check out the unit tests for Named Placeholders and List Formatter to see plenty more examples!
It even has several forms of error-handling (ignore errors, output errors, throw errors).
Note: the named placeholder feature uses reflection and/or dictionary lookups, so you can deserialize the JSON into C# objects or nested Dictionaries, and it will work great!
Here is how I would do it:
Change your template to this format Hi {Name.First}
Now create a JavaScriptSerializer to convert JSON in Dictionary<string, object>
JavaScriptSerializer jss = new JavaScriptSerializer();
dynamic d = jss.Deserialize(data, typeof(object));
Now the variable d has the values of your JSON in a dictionary.
Having that you can run your template against a regex to replace {X.Y.Z.N} with the keys of the dictionary, recursively.
Full Example:
public void Test()
{
// Your template is simpler
string template = "Hi {Name.First}";
// some JSON
string data = #"{""Name"":{""First"":""Jack"",""Last"":""Smith""}}";
JavaScriptSerializer jss = new JavaScriptSerializer();
// now `d` contains all the values you need, in a dictionary
dynamic d = jss.Deserialize(data, typeof(object));
// running your template against a regex to
// extract the tokens that need to be replaced
var result = Regex.Replace(template, #"{?{([^}]+)}?}", (m) =>
{
// Skip escape values (ex: {{escaped value}} )
if (m.Value.StartsWith("{{"))
return m.Value;
// split the token by `.` to run against the dictionary
var pieces = m.Groups[1].Value.Split('.');
dynamic value = d;
// go after all the pieces, recursively going inside
// ex: "Name.First"
// Step 1 (value = value["Name"])
// value = new Dictionary<string, object>
// {
// { "First": "Jack" }, { "Last": "Smith" }
// };
// Step 2 (value = value["First"])
// value = "Jack"
foreach (var piece in pieces)
{
value = value[piece]; // go inside each time
}
return value;
});
}
I didn't handle exceptions (e.g. the value couldn't be found), you can handle this case and return the matched value if it wasn't found. m.Value for the raw value or m.Groups[1].Value for the string between {}.
Have you thought of using Javascript as your scripting language? I had great success with Jint, although the startup cost is high. Another option is Jurassic, which I haven't used myself.
If you happen to have a Web Application, using Razor maybe an idea, see here.
Using Regex or any sort of string parsing can certainly work for trivial things, but can get painful when you want logic or even just basic hierarchies. If you deserialize your JSON into nested Dictionaries, you can build a parser relatively easily:
// Untested and error prone, just to illustrate the concept
var parts = "parentObj.childObj.property".split('.');
Dictionary<object,object> current = yourDeserializedObject;
foreach(var key in parts.Take(parts.Length-1)){
current = current[key];
}
var value = current[parts.Last()];
Just whatever you do, don't do XSLT. Really, if XSLT is the answer then the question must have been really desperate :)
Why not us nvelocity or something?