วันอาทิตย์ที่ 3 พฤศจิกายน พ.ศ. 2562

ทำ Ribbon บน C# Form

การทำ Ribbon ใน C# Form จะใช้ ได้ ควรเป็น Dotnet 4.6.1 ในเลือกไปที่
Nuget Manager เลือก Ribbon WinForms



แต่มักจะมีปัญหาว่า Ribbon อาจจะไม่แสดง ใน Toolbox  ต้องไป set ใน Toolbox Show all

และเลือก Choose Items เลือก Ribbon ถ้าไม่เจอต้อง Browse Reference ไปที่ DLL




วางใน Form เลือก ลง Ribbon บนจอจะให้ใส่ Tab และ Panel และ ใส่ Button ได้ และกดสร้าง Event ได้ตามปรกติ


วันเสาร์ที่ 5 ตุลาคม พ.ศ. 2562

C# จัดการ Zip file

C# จัดการ Zip ให้ Reference system.io.compression และ system.io.compression.filesystem

ในการ List ไฟล์ใน Zip
   private void button_open_Click(object sender, EventArgs e)
        {
            listBox1.Items.Clear();

            string zipPath = label1.Text;

                using (ZipArchive archive = ZipFile.OpenRead(zipPath))
            {
                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                        // Gets the full path to ensure that relative segments are removed.
                        string destinationPath =  entry.FullName;
                        listBox1.Items.Add(destinationPath);
            
                }
            }
        }
ในการ Extract File ออกมา แล้ว edit ด้วย Notepad

  private void button_Edit_Click(object sender, EventArgs e)
        { // select file
            int index = listBox1.SelectedIndex;
            string sFile = listBox1.Items[index].ToString();
            // extract file
            string exPath = "c:/temp/";
            string zipPath = label1.Text;

            using (ZipArchive archive = ZipFile.OpenRead(zipPath))
            {
                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                    // Gets the full path to ensure that relative segments are removed.
                    string destinationPath = exPath+entry.FullName;
                    if (entry.FullName == sFile)
                    { // extrac file

                        entry.ExtractToFile(destinationPath,true);
                        label_msg.Text = "Extract to " + destinationPath;
                        string editor = @"C:\WINDOWS\NOTEPAD.EXE";
                        Process.Start(editor, destinationPath);
                    }
                    
                }
            }
        }


ในการ Delete File เดิม และ จัดเก็บ


 private void button_Save_Click(object sender, EventArgs e)
        {
            int index = listBox1.SelectedIndex;
            string sFile = listBox1.Items[index].ToString();
            // extract file
            string exPath = "c:/temp/";
            string zipPath = label1.Text;

            using (ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Update))
            {
                // delete old file
                foreach (ZipArchiveEntry entry in archive.Entries)
                {
                    // Gets the full path to ensure that relative segments are removed.
                    string destinationPath1 = exPath + entry.FullName;
                    if (entry.FullName == sFile)
                    { // extrac file
                        entry.Delete();
                        label_msg.Text = "delete zip " + sFile;
                        break;
                    }

                }

            }
            // update 
            using (ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Update))
            { 
                string destinationPath = exPath + sFile;
                    archive.CreateEntryFromFile(destinationPath, sFile);
                    
                    label_msg.Text = "Zip to " + destinationPath;
                    return;
                
            }
        }

Revit หา Duct ตัด กำแพง

หลักการ เป็นการ หา Curve ของ Duct เป็น XYZ และ ตัดกับ Face ของ กำแพง

 public static void ductIntersectWall(ExternalCommandData cmd)
        {
            UIApplication uiapp = cmd.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Autodesk.Revit.DB.Document CachedDoc = uidoc.Document;
            {
                 FilteredElementCollector WallCollector = new FilteredElementCollector(CachedDoc);
                WallCollector.OfClass(typeof(Wall));
                List<Wall> walls = WallCollector.Cast<Wall>().ToList();// หา กำแพง

                FilteredElementCollector DuctCollector = new FilteredElementCollector(CachedDoc);
                DuctCollector.OfClass(typeof(Duct)); // หา Duct

                List<Duct> ducts = DuctCollector.Cast<Duct>().ToList();
                List<XYZ> points = new List<XYZ>();

                foreach (Duct d in ducts)
                {
                    foreach (Wall w in walls)
                    {
                        LocationCurve lc = d.Location as LocationCurve;
                        Curve ductCurve = lc.Curve; // แต่ละ Duct หา เป็น เส้นCurve

                        XYZ intersection = null;

                        List<Face> wallFaces = FindWallFace(w); // หา หน้า Face ของกำแพง

                        foreach (Face f in wallFaces)
                        {
                            intersection = FindFaceCurve(ductCurve, f); // หาจุดตัด Face กับ เส้น duct
                            if (null != intersection)
                                points.Add(intersection);
                        }
                    }
                }

                StringBuilder sb = new StringBuilder();

                foreach (XYZ p in points)
                {
                    sb.AppendLine(p.ToString()); // เก็บ ไปแสดงค่าจุดตัด
                }
                TaskDialog.Show("duct inter wall(ft)", sb.ToString());
             }
            }
        // support function
        public static List<Face> FindWallFace(Wall wall)
        {
            List<Face> normalFaces = new List<Face>();

            Options opt = new Options();
            opt.ComputeReferences = true;
            opt.DetailLevel = ViewDetailLevel.Fine;

            GeometryElement e = wall.get_Geometry(opt);

            foreach (GeometryObject obj in e)
            {
                Solid solid = obj as Solid;

                if (solid != null && solid.Faces.Size > 0)
                {
                    foreach (Face face in solid.Faces)
                    {
                        PlanarFace pf = face as PlanarFace;
                        if (pf != null)
                        {
                            normalFaces.Add(pf);
                        }
                    }
                }
            }
            return normalFaces;
        }

        public static XYZ FindFaceCurve(Curve DuctCurve, Face WallFace)
        {
            //The intersection point
            IntersectionResultArray intersectionR = new IntersectionResultArray();//Intersection point set

            SetComparisonResult results;//Results of Comparison

            results = WallFace.Intersect(DuctCurve, out intersectionR);

            XYZ intersectionResult = null;//Intersection coordinate

            if (SetComparisonResult.Disjoint != results)
            {
                if (intersectionR != null)
                {
                    if (!intersectionR.IsEmpty)
                    {
                        intersectionResult = intersectionR.get_Item(0).XYZPoint;
                    }
                }
            }
            return intersectionResult;

        }

วันเสาร์ที่ 28 กันยายน พ.ศ. 2562

การอ่านเขียน ค่า Registry ด้วย Autolisp

ในการอ่านค่า Registry ด้วย Autolisp ต้องใช้ตามนี้ ใน Help ของ Autodesk ผิด

 (vl-registry-read "HKEY_CURRENT_USER\\Software\\<ชื่อกับ sub ที่ต้องการ>" "<key>")

กรณี Write
(vl-registry-write "HKEY_CURRENT_USER\\Software\\<ชื่อกับ sub ที่ต้องการ>" "<key>" "value")

วันพุธที่ 18 กันยายน พ.ศ. 2562

วิธีอ่านชื่อ Family and Type ขอ Element


  ในกรณีที่เราต้องการ ดู Family name ,Type name ของ Family จาก Element Id          


               Element e = doc.GetElement(elementId);

                Element et = doc.GetElement(e.GetTypeId()); // ไปอ่าน type 
                ElementType etype = et as ElementType;
                if (etype == null)
                    return;

              // ชื่อ type เช่น 100x100 mm.
             string typeN= etype.Name;

                FamilyInstance elFamInst = e as FamilyInstance;
                Family elFam = elFamInst.Symbol.Family;
             // ชื่อ Family เช่น Rectangular Column
            string fname =elFam.Name;
               

ค้น ทั้ง Element และ Type ใช้ c# Linq ใน Revit


Revit เก็บ Object ใน รูปแบบ element โดยมี Key เป็น ElementId การค้นมี tool ชื่อ FilteredElementCollector  
การใช้งาน ให้ใช้คู่กับ Linq 
OfClass หมายถึง type ที่ต้องการหา เช่น typeof( Family) หรือ typeof(DimensionType) เป็นต้น และ กำหนด ชื่อ จาก targetname

public static Element FindElementByName(
    Document doc,
    Type targetType,
    string targetName)
        {
            return new FilteredElementCollector(doc)
              .OfClass(targetType)
              .FirstOrDefault<Element>(
                e => e.Name.Equals(targetName));
        }

Load Family จาก File ใน Revit

การ Load Family จากไฟล์ .จะมีชื่อกับ Type ตาม file

string FamilyName = "ชื่อ Family";
string FamilyPath = "c:/temp/ชื่อ Family.rfa";


  Family family = FindElementByName( doc, typeof(Family), FamilyName) as Family;

            if (family != null) return; //already exist

            if (!File.Exists(FamilyPath))
            {
               MessageBox.Show(string.Format(
                  "Please ensure that the sample table "
                  + "family file '{0}' exists in '{1}'.",
                  FamilyName, path));

                return ;
            }

            // Load family from file:

            using (Transaction tx = new Transaction(doc))
            {
                tx.Start("Load Family");
                doc.LoadFamily(FamilyPath, out family);
                
                tx.Commit();
            }

วันอังคารที่ 17 กันยายน พ.ศ. 2562

Json ใน SQL Server 2017 และสร้าง Class ใน C#

Json ใช้ในระบบของ Javascript และ การติดต่อ ระหว่าง Cloud ระหว่างหลายภาษา เช่น python, java, dotnet. SQL Server 2017 มี Feature นี้ ด้วย

ก่อนอื่นต้อง Upgrade database ก่อน
ALTER DATABASE [TestDB] SET COMPATIBILITY_LEVEL = 130

ทดลอง Data ที่เป็น JSON

DECLARE @JSONData AS NVARCHAR(4000)
SET @JSONData = N'{
    "EmployeeInfo":{
        "FirstName":"Somchai",
        "LastName":"Saema",
        "Code":"CODED",
        "Addresses":[
            { "Address":"1234", "City":"Bangkok", "State":THAILAND"},
            { "Address":"5678", "City":"Nonthaburi", "State":"THAILAND"}
        ]
    }
}'

ตัวเช็ก ว่าเป็น Json หรือไม่
select isjson(@jsondata)
อ่านค่า
select json_value(@jsondata,'$.EmployeeInfo.Addresses[1].Address')

ดูว่าใช่หรือไม่
select isjson(@jsondata)
ดูค่า
select json_value(@jsondata,'$.EmployeeInfo.Addresses[1].Address')
select JSON_query(@jsondata,'$.EmployeeInfo.Addresses')
แก้
SET @JSONData = JSON_MODIFY(@JSONData,'$.EmployeeInfo.FirstName', 'Name'')
select json_value(@jsondata,'$.EmployeeInfo.FirstName')
เพิ่ม
SET @JSONData = JSON_MODIFY(@JSONData,'$.EmployeeInfo.MiddleName', 'NNK')
select JSON_value(@jsondata,'$.EmployeeInfo.MiddleName')

select * from openjson(@jsondata)
Select JSON_query(@jsondata,'$.EmployeeInfo.Addresses')
แก้ ค่า
SET @JSONData = JSON_MODIFY(@JSONData,'$.EmployeeInfo.FirstName', 'Rakesh')
select json_value(@jsondata,'$.EmployeeInfo.FirstName')
เพิ่ม field
SET @JSONData = JSON_MODIFY(@JSONData,'$.EmployeeInfo.MiddleName', 'MAI')
select JSON_value(@jsondata,'$.EmployeeInfo.MiddleName')

select * from openjson(@jsondata)

การ Update จะมา เป็น JSON ต้อง แตกออก โดยใช้ OPENJSON
DECLARE @v AS NVARCHAR(4000)
set @v='{"UserID":7,"UserName":"Somsak","RegDate":"2017-01-23T00:00:00"}'

ถ้า select * from OPENJSON(@v)
จะได้
 key .  value . type
UserId     7    2
UserName Somsak 1
RegDate 2017-01-23T00:00:00 1

บาง ค่า เราใช้ Where ได้เลย

select * from OPENJSON(@v) with ( UserID int '$.UserID', UserName varchar(50) '$.UserName', RegDate datetime '$.RegDate')
ผล
UserId UserName  RegDate
7 Somsak 2017-01-23 00:00:00.000


แล้วจึงใช้ Insert หรือ Update ได้ แทน @v ลงใน $$JSON$$
    string cmdtext = "INSERT INTO [temp_Users] ([UserID],[UserName],[RegDate])"+
                "Select UserID,UserName,RegDate from OPENJSON('$$JSON$$')with ( UserID int '$.UserID', UserName varchar(50) '$.UserName', RegDate datetime '$.RegDate')";

        

ในกรณีที่ได้ ผลจาก query มาแล้วต้องการเขียนเป็นโปรแกรมแบบ Class (no sql) ก็ทำได้โดยเข้า Web
json2charp.com และ เลือก Quicktype (ใน Visual Studio ต้องลง Nuget ของ Newtonsoft ก่อน)

เช่น ผลจาก Query
[{"UserID":1,"UserName":"Phar","RegDate":"2017-01-23T00:00:00"},{"UserID":2,"UserName":"Som"},{"UserID":3,"UserName":"Cho"}]



Generate จาก json2charp.com จะได้ Class .ให้ไปเปลี่ยน Welcome  Class เอา

namespace QuickType
{
    using System;
    using System.Collections.Generic;

    using System.Globalization;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Converters;

    public partial class Welcome
    {
        [JsonProperty("UserID")]
        public long UserId { get; set; }

        [JsonProperty("UserName")]
        public string UserName { get; set; }

        [JsonProperty("RegDate", NullValueHandling = NullValueHandling.Ignore)]
        public DateTimeOffset? RegDate { get; set; }
    }

    public partial class Welcome
    {
        public static Welcome[] FromJson(string json) => JsonConvert.DeserializeObject<Welcome[]>(json, QuickType.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this Welcome[] self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
    }

    internal static class Converter
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
        {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters =
            {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            },
        };
    }
}

เวลาใช้ใน SQL  c# จะเป็นแบบนี้


 string connString = @"Data Source=127.0.0.1\sql_server_demo;User ID=sa;Password=???;Initial Catalog=MyDb;Integrated Security=";
           
            string cmdtext = "SELECT *  FROM [temp_Users] FOR JSON AUTO";
            using (SqlConnection conn = new SqlConnection(connString))
            {
                conn.Open();

                using (SqlCommand cmd = new SqlCommand(cmdtext, conn))
                {

                    
                   SqlDataReader rdr= cmd.ExecuteReader();

                    // Get the values
                    while (rdr.Read())
                    {
                        //listBox1.Items.Add(rdr[0]);
                        textBox1.Text =rdr[0].ToString();
                      QuickType.Welcome[] datas=  QuickType.Welcome.FromJson(textBox1.Text);
                        string username = datas[0].UserName;
                    }

                }
            }
        }

ลดเวลาในการเขียนและ Validate โปรแกรมได้มาก


วันเสาร์ที่ 7 กันยายน พ.ศ. 2562

ติดตั้ง SQL Server ด้วย docker

Microsoft ออก SQL Server linux ใช้งาน ง่ายมาก
ติดตั้ง Docker แล้วเลือกติดตั้ง sqlserver Linux แล้ว start docker ก่อน

docker pull microsoft/mssql-server-linux
docker ps -a จะได้ <Key> เป็นตัวเลข
docker run -d --name sql_server_demo -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=Your_Password' -p 1433:1433 microsoft/mssql-server-linux

docker cp <Path>you_database.mdf <key>:/var/opt/mssql/data

แล้ว ลง SQL Server Management
เลือก attach database

ปิด Service
docker container stop sql_server_demo

วันอาทิตย์ที่ 4 สิงหาคม พ.ศ. 2562

sqlite ใน Memory และ ความเร็ว

Sqlite ใน C# สามารถทำงานใน Mode in Memory จะได้ ความเร็ว เพิ่ม แต่ ต้องระวัง ถ้าจบไม่ได้ Copy ลง file ก็หายหมด

สรุปความเร็ว

Insert/Update เร็ว . File กว่า 20%
Select ไม่มี Where เร็วกว่า File 1000% หรือ 10 เท่า
Select มี Where เร็ว กว่า File 200-350% หรือ ประมาณ 3 เท่า

ตัวอย่าง Code



      static void ReadData(SQLiteConnection conn)
        {
            SQLiteDataReader sqlite_datareader;
            SQLiteCommand sqlite_cmd;
            sqlite_cmd = conn.CreateCommand();
            System.Random rng = new Random();
            int rnNumber = rng.Next(1, 1000); // select by where
            sqlite_cmd.CommandText = "SELECT * FROM Person Where ID="+rnNumber;

            sqlite_datareader = sqlite_cmd.ExecuteReader();
            while (sqlite_datareader.Read())
            {
                string myreader = sqlite_datareader.GetInt32(0).ToString();//.GetString(0);
                //Console.WriteLine(myreader);
            }
          //  conn.Close();
        }
        static void DropTable(SQLiteConnection conn)
        {

            SQLiteCommand sqlite_cmd;
            string Createsql = "Drop TABLE Person;";
         sqlite_cmd = conn.CreateCommand();
            sqlite_cmd.CommandText = Createsql;
            sqlite_cmd.ExecuteNonQuery();

        }
        private void button1_Click(object sender, EventArgs e)
        {
            using (SQLiteConnection connection = new SQLiteConnection(
           "Data Source=:memory:;"
         //  "Data Source=C:/test/DataSQLite/myDb.db"
           ))

               

            {
                connection.Open();

         //       connection.CreateModule(new SQLiteModuleEnumerable(
          //        "sampleModule", new string[] { "one", "two", "three" }));

                using (SQLiteCommand command = connection.CreateCommand())
                {
                    command.CommandText =
                        "CREATE TABLE IF NOT EXISTS Person (ID INTEGER,FirstName TEXT, LastName TEXT);";

                    command.ExecuteNonQuery();
                }

                using (SQLiteCommand command = connection.CreateCommand())
                {
                    var stopwatch = new Stopwatch();
                    stopwatch.Start();

                    using (var cmd = new SQLiteCommand(connection))
                    {
                        using (var transaction = connection.BeginTransaction())
                        {
                            // 100,000 inserts
                            for (var i = 0; i < 1000000; i++)
                            {
                                cmd.CommandText =
                                    string.Format("INSERT INTO Person (ID,FirstName, LastName) VALUES ({0},'SOM', 'LIVE');",i);
                                cmd.ExecuteNonQuery();
                            }

                            transaction.Commit();
                            
                        }
                    }

                    listBox1.Items.Add(string.Format("{0} seconds with one transaction.",
                      stopwatch.Elapsed.TotalSeconds));
                    // try select
                    stopwatch.Reset();
                    stopwatch.Start();
                    for (int i = 0; i < 100; i++)
                    {
                        ReadData(connection);
                    }
                    listBox1.Items.Add(string.Format("{0} seconds with one transaction.",
                      stopwatch.Elapsed.TotalSeconds));
                    stopwatch.Stop();
                }
                DropTable(connection);
                connection.Close();
            }

  

วันพฤหัสบดีที่ 25 กรกฎาคม พ.ศ. 2562

การใช้ OpenCV กับ python ทำ Face Recognition

Python เป็นภาษาที่มีคนใช้ทั่วโลกมากที่สุด และ มี Open Source จำนวนมาก ทำให้ทำงาน บางอย่างได้ทันที
OpenCV เป็น โปรแกรม OpenSource ยอดนิยม สำหรับ Image และ Video Version ที่ติดตั้งให้ติดตั้งหลังจาก ติดตั้ง Python ก่อน ใช้ Version 3 ถ้ามี Nvidia ให้ลง Version ที่ Support Cura จะได้ ความเร็วในการ run แต่ติดตั้งง่ายให้ ใช้ Intel

การติดตั้ง Python ให้ ติดตั้งจาก Tool ของ Anaconda จะง่ายสุด

เวลา run อาจจะใช้    Jupyter notebook หรือไม่นั้นก็ Save เป็น facerec.py และ run จาก command line
python facerec.py ก็ได้

Code นี้ทดลองใน OSX ต้องมี กล้อง webcam ด้วย Code นี้ยังไม่ใช่ AI แต่เป็น Algorithm Detect หน้า
# cv2 เป็นตัวแทนของ opencv3 
import cv2
import sys
#haarcascade_frontalface_default.xml  ไป download from opencv website
cascPath = "haarcascade_frontalface_default.xml"
print cascPath
faceCascade = cv2.CascadeClassifier(cascPath)
# เปิด กล้อง
cv2.namedWindow("preview")
video_capture = cv2.VideoCapture(0)

# loop สำหรับ Recognition 
while True:
    # ที่ละ Frame
    ret, frame = video_capture.read()

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30)
    )

    # วาดสี่เหลี่ยมหน้า
    for (x, y, w, h) in faces:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)

    # แสดงรูป
    cv2.imshow('Video', frame)
# กด x ออก
    if cv2.waitKey(1) & 0xFF == ord('x'):
        break

#ปิด Video
video_capture.release()
cv2.destroyAllWindows()

วันเสาร์ที่ 15 มิถุนายน พ.ศ. 2562

ให้ C# Load Excel โดยไม่ Reference

ใน C# จะมี ปัญหา บางครั้งในการ load และ Start excel มักจะเกิด Interop Error ไม่ตรงกับ ที่ Install

วิธีแก้ให้ ใช้ Dynamic


Type typeExcel = Type.GetTypeFromProgID("Excel.Application");

dynamic excel = Activator.CreateInstance(typeExcel);
excel.Visible = true;

dynamic workbooks = excel.Workbooks;
workbooks.Add();
dynamic workSheet = excel.ActiveSheet;
workSheet.Cells[1, 1] = "Names";
workSheet.Cells[1, 2] = "Age";

   

วันพฤหัสบดีที่ 30 พฤษภาคม พ.ศ. 2562

ข้อระวังความไม่ compatible ระหว่าง Microsoft Json และ NewtonSoft Json

JSON ใช้เป็นหลักในการเชื่อมระหว่างโปรแกรมกับ Cloud โดยผ่าน file หรือ Http

Json มี 2 ค่าย หลัก ได้แก่ Newtonsoft กับ Microsoft
ในการใช้งาน จะมี Class Generator สำหรับ Json ที่
Quicktype.Io จะสร้าง Class สำหรับ Newtonsoft แต่จะไม่ compatible กับ Microsoft
ตัวที่ต้องระวัง คือ DateTime
ให้เปลี่ยนใน
Class เป็น String เช่น

 public partial class CustomVisionJson
    {
        //[JsonProperty("id")]
        public Guid id { get; set; }

        //[JsonProperty("project")]
        public Guid project { get; set; }

        //[JsonProperty("iteration")]
        public Guid iteration { get; set; }

        //[JsonProperty("created")] // previous type is DateTimeOffset
        public string created { get; set; }

      //  [JsonProperty("predictions")]
        public Prediction [] predictions { get; set; }
    }


การ Serialize และ DeSerialize ของ Microsoft

     public static CaptureInfo FromJson(string json)
        {
            return doDeserialize(json, typeof(CaptureInfo)) as CaptureInfo;
        }
        public static string ConvertToJSON(object obj)
        {
            string s = doSerialize(obj);
            return s;
        }
        public static string doSerialize(object obj)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            using (StreamReader reader = new StreamReader(memoryStream))
            {
                DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
                serializer.WriteObject(memoryStream, obj);
                memoryStream.Position = 0;
                return reader.ReadToEnd();
            }
        }

        public static object doDeserialize(string xml, Type toType)
        {
            using (Stream stream = new MemoryStream())
            {
                byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
                stream.Write(data, 0, data.Length);
                stream.Position = 0;
                DataContractJsonSerializer deserializer = new DataContractJsonSerializer(toType);
                return deserializer.ReadObject(stream);
            }
        }

   

ของ Newtonsoft

  public partial class CustomVisionJson
    {
        public static CustomVisionJson FromJson(string json) => JsonConvert.DeserializeObject<CustomVisionJson>(json, QuickType.Converter.Settings);
    }

    public static class Serialize
    {
        public static string ToJson(this CustomVisionJson self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
    }


Newtonsoft จะง่าย กว่า แต่ข้อเสียคือจะ run ใน CS Script ไม่ได้