Compare commits
2 Commits
49eec5b849
...
a9acdc933a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9acdc933a | ||
|
|
73e725b6ec |
@@ -175,30 +175,168 @@ namespace Nodify.Calculator.Execution.Resolvers
|
|||||||
var raw = ctx.Read(input);
|
var raw = ctx.Read(input);
|
||||||
if (string.IsNullOrEmpty(raw)) return null;
|
if (string.IsNullOrEmpty(raw)) return null;
|
||||||
|
|
||||||
// Ensure we have valid JSON — if not, serialize the raw value
|
ctx.Log($"[PARSE JSON] Raw input: {(raw.Length > 120 ? raw.Substring(0, 120) + "..." : raw)}");
|
||||||
|
|
||||||
|
// Ensure we have valid JSON
|
||||||
var trimmed = raw.Trim();
|
var trimmed = raw.Trim();
|
||||||
bool isJson = (trimmed.StartsWith("{") && trimmed.EndsWith("}"))
|
bool isJson = (trimmed.StartsWith("{") && trimmed.EndsWith("}"))
|
||||||
|| (trimmed.StartsWith("[") && trimmed.EndsWith("]"))
|
|| (trimmed.StartsWith("[") && trimmed.EndsWith("]"))
|
||||||
|| (trimmed.StartsWith("\"") && trimmed.EndsWith("\""));
|
|| (trimmed.StartsWith("\"") && trimmed.EndsWith("\""));
|
||||||
if (!isJson)
|
if (!isJson)
|
||||||
{
|
{
|
||||||
try
|
try { raw = Newtonsoft.Json.JsonConvert.SerializeObject(raw); }
|
||||||
{
|
|
||||||
raw = Newtonsoft.Json.JsonConvert.SerializeObject(raw);
|
|
||||||
}
|
|
||||||
catch { /* keep raw as-is */ }
|
catch { /* keep raw as-is */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and re-serialize to ensure clean JSON output
|
var targetType = (node is ParseJsonOperationViewModel pj) ? pj.SelectedTargetType : "object";
|
||||||
|
ctx.Log($"[PARSE JSON] Target type: {targetType}");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var parsed = Newtonsoft.Json.JsonConvert.DeserializeObject(raw);
|
var token = JToken.Parse(raw);
|
||||||
return Newtonsoft.Json.JsonConvert.SerializeObject(parsed);
|
|
||||||
}
|
if (targetType == "object")
|
||||||
catch
|
return token.ToString(Formatting.None);
|
||||||
|
|
||||||
|
if (targetType.StartsWith("List<") && targetType.EndsWith(">"))
|
||||||
{
|
{
|
||||||
return raw;
|
var modelName = targetType.Substring(5, targetType.Length - 6);
|
||||||
|
var modelProps = LoadModelProperties(modelName, ctx);
|
||||||
|
var array = FindArray(token);
|
||||||
|
if (array == null)
|
||||||
|
{
|
||||||
|
ctx.Log($"[PARSE JSON] ERROR: Could not find an array in the input to convert to {targetType}.", logType.Error);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (modelProps != null && modelProps.Length > 0)
|
||||||
|
{
|
||||||
|
var projected = new JArray();
|
||||||
|
foreach (var item in array)
|
||||||
|
{
|
||||||
|
var projectedItem = ProjectToModel(item, modelProps);
|
||||||
|
if (projectedItem != null)
|
||||||
|
projected.Add(projectedItem);
|
||||||
|
else
|
||||||
|
ctx.Log($"[PARSE JSON] WARNING: An array item could not be projected to {modelName}, skipping.", logType.Warning);
|
||||||
|
}
|
||||||
|
ctx.Log($"[PARSE JSON] Parsed {projected.Count} items as List<{modelName}>.");
|
||||||
|
return projected.ToString(Formatting.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Log($"[PARSE JSON] Model file for \"{modelName}\" not found, returning raw array.");
|
||||||
|
return array.ToString(Formatting.None);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Single model type
|
||||||
|
var modelProps = LoadModelProperties(targetType, ctx);
|
||||||
|
var obj = FindObject(token);
|
||||||
|
if (obj == null)
|
||||||
|
{
|
||||||
|
ctx.Log($"[PARSE JSON] ERROR: Could not find an object in the input to convert to {targetType}.", logType.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelProps != null && modelProps.Length > 0)
|
||||||
|
{
|
||||||
|
var projected = ProjectToModel(obj, modelProps);
|
||||||
|
if (projected == null)
|
||||||
|
{
|
||||||
|
ctx.Log($"[PARSE JSON] ERROR: Failed to project object to {targetType}.", logType.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ctx.Log($"[PARSE JSON] Parsed object as {targetType} with {projected.Count} properties.");
|
||||||
|
return projected.ToString(Formatting.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Log($"[PARSE JSON] Model file for \"{targetType}\" not found, returning raw object.");
|
||||||
|
return obj.ToString(Formatting.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
ctx.Log($"[PARSE JSON] ERROR: {ex.Message}", logType.Error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads property names from a model .cs file in CustomModels directory.
|
||||||
|
/// </summary>
|
||||||
|
private static string[] LoadModelProperties(string modelName, IValueResolutionContext ctx)
|
||||||
|
{
|
||||||
|
var modelPath = System.IO.Path.Combine(ProjectManager.ProjectDirectory, "CustomModels", modelName + ".cs");
|
||||||
|
if (!System.IO.File.Exists(modelPath)) return null;
|
||||||
|
|
||||||
|
var lines = System.IO.File.ReadAllLines(modelPath);
|
||||||
|
var props = lines
|
||||||
|
.Select(l => l.Trim())
|
||||||
|
.Where(l => l.StartsWith("public ") && l.Contains("{ get;"))
|
||||||
|
.Select(l =>
|
||||||
|
{
|
||||||
|
var parts = l.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
return parts.Length >= 3 ? parts[2] : null;
|
||||||
|
})
|
||||||
|
.Where(p => p != null)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
ctx.Log($"[PARSE JSON] Loaded model \"{modelName}\" with properties: {string.Join(", ", props)}");
|
||||||
|
return props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Projects a JSON token to a JObject containing only the specified model properties (case-insensitive).
|
||||||
|
/// Unwraps a "data" envelope if needed.
|
||||||
|
/// </summary>
|
||||||
|
private static JObject ProjectToModel(JToken token, string[] modelProps)
|
||||||
|
{
|
||||||
|
var source = token as JObject;
|
||||||
|
if (source == null) return null;
|
||||||
|
|
||||||
|
// If the object has a single wrapper property with an inner object, unwrap it
|
||||||
|
if (source.Count == 1)
|
||||||
|
{
|
||||||
|
var first = source.Properties().First();
|
||||||
|
if (first.Value is JObject inner)
|
||||||
|
source = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = new JObject();
|
||||||
|
foreach (var propName in modelProps)
|
||||||
|
{
|
||||||
|
var match = source.Properties()
|
||||||
|
.FirstOrDefault(p => string.Equals(p.Name, propName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
result[propName] = match != null ? match.Value.DeepClone() : JValue.CreateNull();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JArray FindArray(JToken token)
|
||||||
|
{
|
||||||
|
if (token is JArray arr) return arr;
|
||||||
|
if (token is JObject obj)
|
||||||
|
{
|
||||||
|
foreach (var prop in obj.Properties())
|
||||||
|
if (prop.Value is JArray innerArr) return innerArr;
|
||||||
|
foreach (var prop in obj.Properties())
|
||||||
|
if (prop.Value is JObject inner)
|
||||||
|
foreach (var innerProp in inner.Properties())
|
||||||
|
if (innerProp.Value is JArray deepArr) return deepArr;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JObject FindObject(JToken token)
|
||||||
|
{
|
||||||
|
if (token is JObject obj)
|
||||||
|
{
|
||||||
|
var props = obj.Properties().ToList();
|
||||||
|
if (props.Count == 1 && props[0].Value is JObject inner)
|
||||||
|
return inner;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,6 +230,9 @@ namespace Nodify.Calculator
|
|||||||
if (!string.IsNullOrEmpty(resolved))
|
if (!string.IsNullOrEmpty(resolved))
|
||||||
{
|
{
|
||||||
OnLogMe?.Invoke($"[RESOLVE] Resolver returned: {(resolved.Length > 80 ? resolved.Substring(0, 80) + "..." : resolved)}");
|
OnLogMe?.Invoke($"[RESOLVE] Resolver returned: {(resolved.Length > 80 ? resolved.Substring(0, 80) + "..." : resolved)}");
|
||||||
|
// Cache so subsequent reads hit the fast path
|
||||||
|
if (srcNodeId != null)
|
||||||
|
outputs[srcNodeId] = resolved;
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
OnLogMe?.Invoke($"[RESOLVE] Resolver returned null/empty, trying next...", logType.Warning);
|
OnLogMe?.Invoke($"[RESOLVE] Resolver returned null/empty, trying next...", logType.Warning);
|
||||||
@@ -246,6 +249,8 @@ namespace Nodify.Calculator
|
|||||||
if (!string.IsNullOrEmpty(passthrough))
|
if (!string.IsNullOrEmpty(passthrough))
|
||||||
{
|
{
|
||||||
OnLogMe?.Invoke($"[RESOLVE] Passthrough returned: {(passthrough.Length > 80 ? passthrough.Substring(0, 80) + "..." : passthrough)}");
|
OnLogMe?.Invoke($"[RESOLVE] Passthrough returned: {(passthrough.Length > 80 ? passthrough.Substring(0, 80) + "..." : passthrough)}");
|
||||||
|
if (srcNodeId != null)
|
||||||
|
outputs[srcNodeId] = passthrough;
|
||||||
return passthrough;
|
return passthrough;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user