{"id":24485420,"url":"https://github.com/sadomovalex/camlex","last_synced_at":"2025-10-27T09:43:11.918Z","repository":{"id":51386215,"uuid":"99702030","full_name":"sadomovalex/camlex","owner":"sadomovalex","description":"Camlex simplifies creation of CAML queries for SharePoint (2007, 2010, 2013, 2016, 2019, Online) by using expression trees and lambda expressions","archived":false,"fork":false,"pushed_at":"2024-09-25T07:41:51.000Z","size":10894,"stargazers_count":55,"open_issues_count":8,"forks_count":10,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-10T01:06:47.758Z","etag":null,"topics":["caml","caml-query","lambda-expressions","sharepoint","sharepoint-online"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sadomovalex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-08-08T14:29:17.000Z","updated_at":"2025-04-09T10:05:08.000Z","dependencies_parsed_at":"2024-07-07T11:06:03.091Z","dependency_job_id":null,"html_url":"https://github.com/sadomovalex/camlex","commit_stats":{"total_commits":70,"total_committers":5,"mean_commits":14.0,"dds":"0.24285714285714288","last_synced_commit":"fc6ba66b6c6ea56661a14ad4a0307d6f514f3df3"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sadomovalex%2Fcamlex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sadomovalex%2Fcamlex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sadomovalex%2Fcamlex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sadomovalex%2Fcamlex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sadomovalex","download_url":"https://codeload.github.com/sadomovalex/camlex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137887,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caml","caml-query","lambda-expressions","sharepoint","sharepoint-online"],"created_at":"2025-01-21T14:17:51.752Z","updated_at":"2025-10-27T09:43:06.866Z","avatar_url":"https://github.com/sadomovalex.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Camlex.Net - Home\n\nProject was moved from CodePlex (which is closed since Dec 2017). All further Camlex development and discussions will be hosted here on GitHub.\n\n## Project Description\nCamlex.NET simplifies creating of CAML queries for SharePoint (2007, 2010, 2013, 2016, Online) by using expression trees:\n\n![](https://github.com/sadomovalex/camlex/wiki/images/Documentation_mapping.jpg)\n\nCompiled binaries can be downloaded from Nuget: [Camlex.NET](https://www.nuget.org/packages/Camlex.NET.dll/)\n\nIf you find Camlex.NET useful, you may support its development via ![](https://github.com/sadomovalex/camlex/wiki/images/Home_btn_donate_SM.gif) button on the online version [http://camlex.ru.net](http://camlex.ru.net).\n\n## Client object model\nVersion for Client object model is also available. It can be downloaded also from Nuget: [Camlex.Client](https://www.nuget.org/packages/Camlex.Client.dll/).\n\n## Online version\nYou can convert your existing CAML queries to C# code for Camlex.NET using online service powered by Camlex: [http://camlex.ru.net](http://camlex.ru.net).\n\n## Installation Instructions\nIn order to start working with Camlex.NET, please download and reference Camlex.NET.dll assembly in your project. After that you can create CAML queries using lambda expressions.\nStarting from version 3.0 you can install NuGet package for Camlex from online packages gallery:\n```\nInstall-Package Camlex.NET.dll\n```\n\nThere is also Camlex version which uses CSOM - Camlex.Client. Starting with version 4.0.0.0 there are separate Nuget packages for each Sharepoint version:\n\n**Sharepoint Online:**\n```\nInstall-Package Camlex.Client.dll\n```\n**Sharepoint 2013 on-prem:**\n```\nInstall-Package Camlex.Client.2013\n```\n**Sharepoint 2016 on-prem:**\n```\nInstall-Package Camlex.Client.2016\n```\n**Sharepoint 2019 on-prem:**\n```\nInstall-Package Camlex.Client.2019\n```\n\n## Quick Start\nCamlex.NET is a new technique in Sharepoint development. It abstracts developers from syntax of CAML queries and helps them to concentrate on business tasks. With Camlex.NET developers could think about WHAT they need to do, instead of HOW to do it. It also brings the following advantages for Sharepoint developers:\n* Compile time checking of expressions\n* Natural and intuitive syntax using lambda expressions and fluent interfaces\n* Support for native .Net types (int, strings, bool, DateTime, Guid) and operations (==, !=, \u003e, \u003c, etc)\n* Support for Sharepoint-specific data types\n* Ability to specify non-constant expressions inside filtering conditions (variables, method calls, etc)\n* Fully customizable resulting query object\n* Ability to build dynamic filtering conditions and join them using And/Or logical joins\n* Search by lookup fields using LookupValue or LookupId\n\nNow let's consider some basic scenarios:\n\n**Scenario 1. Simple query**\nSuppose that you need to select all items which have Status field set to Completed (following is the standard syntax of CAML):\n```xml\n\u003cWhere\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"Status\" /\u003e\n    \u003cValue Type=\"Text\"\u003eCompleted\u003c/Value\u003e\n  \u003c/Eq\u003e\n\u003c/Where\u003e\n```\nThis query can be made with Camlex using the following syntax:\n```csharp\nstring caml =\n    Camlex.Query()\n        .Where(x =\u003e (string)x[\"Status\"] == \"Completed\").ToString();\n```\nNotice, other comparison operations like “\u003c”, “\u003c=”, “\u003e”, “\u003e=” are supported as well.\n\n**Scenario 2. Query with “and”/”or” conditions**\nSuppose that you need to select items which have ProductID = 1000 and IsCompleted set to false or null. Syntax of appropriate standard CAML query follows:\n```xml\n\u003cWhere\u003e\n  \u003cAnd\u003e\n    \u003cEq\u003e\n      \u003cFieldRef Name=\"ProductID\" /\u003e\n      \u003cValue Type=\"Integer\"\u003e1000\u003c/Value\u003e\n    \u003c/Eq\u003e\n    \u003cOr\u003e\n      \u003cEq\u003e\n        \u003cFieldRef Name=\"IsCompleted\" /\u003e\n        \u003cValue Type=\"Boolean\"\u003e0\u003c/Value\u003e\n      \u003c/Eq\u003e\n      \u003cIsNull\u003e\n        \u003cFieldRef Name=\"IsCompleted\" /\u003e\n      \u003c/IsNull\u003e\n    \u003c/Or\u003e\n  \u003c/And\u003e\n\u003c/Where\u003e\n```\nWith help of Camlex it could be converted using following natural syntax:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e (int)x[\"ProductID\"] == 1000 \u0026\u0026 ((bool)x[\"IsCompleted\"] == false || x[\"IsCompleted\"] == null))\n            .ToString();\n```\n\n**Scenario 3. Query with DateTime**\nLets suppose that you need to retrieve items which were modified on 01-Jan-2010:\n```xml\n\u003cWhere\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"Modified\" /\u003e\n    \u003cValue Type=\"DateTime\"\u003e2010-01-01T12:00:00Z\u003c/Value\u003e\n  \u003c/Eq\u003e\n\u003c/Where\u003e\n```\nUsing Camlex you can simply write:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e (DateTime)x[\"Modified\"] == new DateTime(2010, 01, 01)).ToString();\n```\n\n**Scenario 4. Query with BeginsWith and Contains operations**\nConsider the query that should return items which Title field starts with “Task” and Project field contains “Camlex”:\n```xml\n\u003cWhere\u003e\n  \u003cAnd\u003e\n    \u003cBeginsWith\u003e\n      \u003cFieldRef Name=\"Title\" /\u003e\n      \u003cValue Type=\"Text\"\u003eTask\u003c/Value\u003e\n    \u003c/BeginsWith\u003e\n    \u003cContains\u003e\n      \u003cFieldRef Name=\"Project\" /\u003e\n      \u003cValue Type=\"Text\"\u003eCamlex\u003c/Value\u003e\n    \u003c/Contains\u003e\n  \u003c/And\u003e\n\u003c/Where\u003e\n```\nYou can achieve result using the following natural syntax:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e ((string)x[\"Title\"]).StartsWith(\"Task\") \u0026\u0026 ((string)x[\"Project\"]).Contains(\"Camlex\"))\n            .ToString();\n```\n\n**Scenario 5. Query with none C# native data types**\nSuppose that you need to retrieve all items modified by Administrator:\n```xml\n\u003cWhere\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"Editor\" /\u003e\n    \u003cValue Type=\"User\"\u003eAdministrator\u003c/Value\u003e\n  \u003c/Eq\u003e\n\u003c/Where\u003e\n```\nNotice that value is of Sharepoint-specific User data type. You can easily achieve the result with alternative string-based syntax:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e x[\"Editor\"] == (DataTypes.User)\"Administrator\")\n                .ToString();\n```\n\n**Scenario 6. Query with sorting (OrderBy)**\nSuppose that you need to select all items which have ID \u003e= 5 and the result should be sorted by Modified field:\n```xml\n\u003cWhere\u003e\n  \u003cGeq\u003e\n    \u003cFieldRef Name=\"ID\" /\u003e\n    \u003cValue Type=\"Integer\"\u003e5\u003c/Value\u003e\n  \u003c/Geq\u003e\n\u003c/Where\u003e\n\u003cOrderBy\u003e\n  \u003cFieldRef Name=\"Modified\" /\u003e\n\u003c/OrderBy\u003e\n```\nYou need to write the following Camlex expression in order to construct this query:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e (int)x[\"ID\"] \u003e= 5)\n        .OrderBy(x =\u003e x[\"Modified\"]).ToString();\n```\n\n**Scenario 7. Query with grouping (GroupBy)**\nSuppose that we need to select items having not-null Status field and result set should be grouped by Author field:\n```xml\n\u003cWhere\u003e\n  \u003cIsNotNull\u003e\n    \u003cFieldRef Name=\"Status\" /\u003e\n  \u003c/IsNotNull\u003e\n\u003c/Where\u003e\n\u003cGroupBy\u003e\n  \u003cFieldRef Name=\"Author\" /\u003e\n\u003c/GroupBy\u003e\n```\nWith Camlex you could simply rewrite it as:\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e x[\"Status\"] != null)\n        .GroupBy(x =\u003e x[\"Author\"]).ToString();\n```\n\n**Scenario 8. Query with non-constant expressions in lvalue and rvalue**\nNon-constant expression gives you more control over CAML. Suppose that you need to select items depending on current locale: for English locale you need to select items which have TitleEng field set to “eng”; for non-English locale you need to select items which have Title field set to “non-eng”. I.e.:\nQuery for English locale:\n```xml\n\u003cWhere\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"TitleEng\" /\u003e\n    \u003cValue Type=\"Text\"\u003eeng\u003c/Value\u003e\n  \u003c/Eq\u003e\n\u003c/Where\u003e\n```\nQuery for non-English locale:\n```xml\n\u003cWhere\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"Title\" /\u003e\n    \u003cValue Type=\"Text\"\u003enon-eng\u003c/Value\u003e\n  \u003c/Eq\u003e\n\u003c/Where\u003e\n```\nIt is not so hard with Camlex:\n```csharp\nbool isEng = true; // or false depending on Thread.CurrentThread.CurrentUICulture\n\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e (string)x[isEng ? \"TitleEng\" : \"Title\"] == (isEng ? \"eng\" : \"non-eng\")).ToString();\n```\n\n**Scenario 9. Dynamic filtering conditions**\nStarting with 2.0 version you can build dynamic filtering conditions and join them using WhereAll/WhereAny methods which use And/Or logical joins respectively.\nSuppose that we need to retrieve all items which contain at least one of the values {“hello”, “greeting”, “hi”} in Title field. I.e. we need to use the following CAML query:\n```xml\n\u003cWhere\u003e\n  \u003cOr\u003e\n    \u003cOr\u003e\n      \u003cContains\u003e\n        \u003cFieldRef Name=\"Title\" /\u003e\n        \u003cValue Type=\"Text\"\u003ehello\u003c/Value\u003e\n      \u003c/Contains\u003e\n      \u003cContains\u003e\n        \u003cFieldRef Name=\"Title\" /\u003e\n        \u003cValue Type=\"Text\"\u003egreeting\u003c/Value\u003e\n      \u003c/Contains\u003e\n    \u003c/Or\u003e\n    \u003cContains\u003e\n      \u003cFieldRef Name=\"Title\" /\u003e\n      \u003cValue Type=\"Text\"\u003ehi\u003c/Value\u003e\n    \u003c/Contains\u003e\n  \u003c/Or\u003e\n\u003c/Where\u003e\n```\nWith Camlex.NET 2.0 we can create lambda expression for each condition and pass them into new WhereAny method:\n```csharp\n// list of tokens\nvar tokens = new List\u003cstring\u003e { \"hello\", \"greeting\", \"hi\" };\nvar expressions = new List\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e();\n\n// create lambda expression for each token in list\nforeach (string t in tokens)\n{\n    string token = t;\n   expressions.Add(x =\u003e ((string)x[\"Title\"]).Contains(token));\n}\n\n// prepare query\nvar caml = Camlex.Query().WhereAny(expressions).ToString();\n```\n\n**Note**: in the example above it is **important** to create local variable \"string token\" inside loop body and assign current token to it. Without it resulting query will use only last token for all conditions. This is because of lazy expressions implementation in .Net 3.5).\n\nAlso it is possible to create more complex dynamic CAML queries using ExpressionsHelper class which allows to combine using Or or And operations other existing conditions:\n```csharp\npublic static class ExpressionsHelper\n{\n    public static Expression\u003cFunc\u003cSPListItem, bool\u003e\u003e CombineAnd(\n        IEnumerable\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e expressions)\n    {\n        ...\n    }\n \n    public static Expression\u003cFunc\u003cSPListItem, bool\u003e\u003e CombineOr(\n        IEnumerable\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e expressions)\n    {\n        ...\n    }\n}\n```\n\nIt can be used like that:\n```csharp\n// Language = Russian or Language = English\nvar languageConditions = new List\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e();\nlanguageConditions.Add(x =\u003e (string)x[\"Language\"] == \"Russian\");\nlanguageConditions.Add(x =\u003e (string)x[\"Language\"] == \"English\");\nvar langExpr = ExpressionsHelper.CombineOr(languageConditions);\n \n// FileLeafRef contains “.docx” or FileLeafRef contains “.xlsx” or ...\nvar extenssionsConditions = new List\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e();\nvar extensions = new[]() {\".docx\", \".xlsx\", \".pptx\"};\nforeach (string e in extensions)\n{\n    string ext = e;\n    extenssionsConditions.Add(x =\u003e ((string)x[\"FileLeafRef\"]).Contains(ext));\n}\nvar extExpr = ExpressionsHelper.CombineOr(extenssionsConditions);\n \n// (Language = Russian or Language = English) and\n// (FileLeafRef contains “.docx” or FileLeafRef contains “.xlsx” or ...)\nvar expressions = new List\u003cExpression\u003cFunc\u003cSPListItem, bool\u003e\u003e\u003e();\nexpressions.Add(langExpr);\nexpressions.Add(extExpr);\n\nstring query = CamlexNET.Camlex.Query().WhereAll(expressions).ToString();\n```\n\nIn result we will get the following CAML:\n```xml\n\u003cWhere\u003e\n  \u003cAnd\u003e\n    \u003cOr\u003e\n      \u003cEq\u003e\n        \u003cFieldRef Name=\"Language\" /\u003e\n        \u003cValue Type=\"Text\"\u003eRussian\u003c/Value\u003e\n      \u003c/Eq\u003e\n      \u003cEq\u003e\n        \u003cFieldRef Name=\"Language\" /\u003e\n        \u003cValue Type=\"Text\"\u003eEnglish\u003c/Value\u003e\n      \u003c/Eq\u003e\n    \u003c/Or\u003e\n    \u003cOr\u003e\n      \u003cOr\u003e\n        \u003cContains\u003e\n          \u003cFieldRef Name=\"FileLeafRef\" /\u003e\n          \u003cValue Type=\"Text\"\u003e.docx\u003c/Value\u003e\n        \u003c/Contains\u003e\n        \u003cContains\u003e\n          \u003cFieldRef Name=\"FileLeafRef\" /\u003e\n          \u003cValue Type=\"Text\"\u003e.xlsx\u003c/Value\u003e\n        \u003c/Contains\u003e\n      \u003c/Or\u003e\n      \u003cContains\u003e\n        \u003cFieldRef Name=\"FileLeafRef\" /\u003e\n        \u003cValue Type=\"Text\"\u003e.pptx\u003c/Value\u003e\n      \u003c/Contains\u003e\n    \u003c/Or\u003e\n  \u003c/And\u003e\n\u003c/Where\u003e\n```\n\n**Scenario 10. List joins**\nStarting with version 4.0 (and Camlex.Client 2.0) it became possible to create list joins and fields projections:\n```csharp\nvar query = new SPQuery();\n \nquery.Query = Camlex.Query().Where(x =\u003e (string)x[\"CustomerCity\"] == \"London\" \u0026\u0026\n    (string)x[\"CustomerCityState\"] == \"UK\").ToString();\n \nquery.Joins = Camlex.Query().Joins()\n    .Left(x =\u003e x[\"CustomerName\"].ForeignList(\"Customers\"))\n    .Left(x =\u003e x[\"CityName\"].PrimaryList(\"Customers\").ForeignList(\"CustomerCities\"))\n    .Left(x =\u003e x[\"StateName\"].PrimaryList(\"CustomerCities\").ForeignList(\"CustomerCityStates\"))\n    .ToString();\n \nquery.ProjectedFields = Camlex.Query().ProjectedFields()\n    .Field(x =\u003e x[\"CustomerCity\"].List(\"CustomerCities\").ShowField(\"Title\"))\n    .Field(x =\u003e x[\"CustomerCityState\"].List(\"CustomerCityStates\").ShowField(\"Title\"))\n    .ToString();\n \nquery.ViewFields = Camlex.Query().ViewFields(x =\u003e new[]() {x[\"CustomerCity\"],\n    x[\"CustomerCityState\"]});\n```\n\nIn this example we will get the following CAML for different SPQuery properties:\nQuery:\n```xml\n\u003cWhere\u003e\n  \u003cAnd\u003e\n    \u003cEq\u003e\n      \u003cFieldRef Name=\"CustomerCity\" /\u003e\n      \u003cValue Type=\"Text\"\u003eLondon\u003c/Value\u003e\n    \u003c/Eq\u003e\n    \u003cEq\u003e\n      \u003cFieldRef Name=\"CustomerCityState\" /\u003e\n      \u003cValue Type=\"Text\"\u003eUK\u003c/Value\u003e\n    \u003c/Eq\u003e\n  \u003c/And\u003e\n\u003c/Where\u003e\n```\n\nJoins:\n```xml\n\u003cJoin Type=\"LEFT\" ListAlias=\"Customers\"\u003e\n  \u003cEq\u003e\n    \u003cFieldRef Name=\"CustomerName\" RefType=\"Id\" /\u003e\n    \u003cFieldRef List=\"Customers\" Name=\"Id\" /\u003e\n  \u003c/Eq\u003e\n\u003c/Join\u003e\n\u003cJoin Type=\"LEFT\" ListAlias=\"CustomerCities\"\u003e\n  \u003cEq\u003e\n    \u003cFieldRef List=\"Customers\" Name=\"CityName\" RefType=\"Id\" /\u003e\n    \u003cFieldRef List=\"CustomerCities\" Name=\"Id\" /\u003e\n  \u003c/Eq\u003e\n\u003c/Join\u003e\n\u003cJoin Type=\"LEFT\" ListAlias=\"CustomerCityStates\"\u003e\n  \u003cEq\u003e\n    \u003cFieldRef List=\"CustomerCities\" Name=\"StateName\" RefType=\"Id\" /\u003e\n    \u003cFieldRef List=\"CustomerCityStates\" Name=\"Id\" /\u003e\n  \u003c/Eq\u003e\n\u003c/Join\u003e\n```\n\nProjectedFields:\n```xml\n\u003cField Name=\"CustomerCity\" Type=\"Lookup\" List=\"CustomerCities\" ShowField=\"Title\" /\u003e\n\u003cField Name=\"CustomerCityState\" Type=\"Lookup\" List=\"CustomerCityStates\" ShowField=\"Title\" /\u003e\n```\n\nViewFields:\n```xml\n\u003cFieldRef Name=\"CustomerCity\" /\u003e\n\u003cFieldRef Name=\"CustomerCityState\" /\u003e\n```\n\n**Scenario 11. Support for LookupMulti field type**\nStarting with version 4.2 (and Camlex.Client 2.2) LookupMulti field type is supported (with possibility to specify LookupId=\"True\" in FieldRef):\n```csharp\nvar caml =\n    Camlex.Query()\n        .Where(x =\u003e x[\"Title\"] \u003e (DataTypes.LookupMultiId)\"5\"\n        \u0026\u0026 x[\"Author\"] == (DataTypes.LookupMultiValue)\"Martin\").ToString();\n```\n\nThis example will produce the following CAML:\n```xml\n\u003cWhere\u003e\n  \u003cAnd\u003e\n    \u003cGt\u003e\n      \u003cFieldRef Name=\"Title\" LookupId=\"True\" /\u003e\n      \u003cValue Type=\"LookupMulti\"\u003e5\u003c/Value\u003e\n    \u003c/Gt\u003e\n    \u003cEq\u003e\n      \u003cFieldRef Name=\"Author\" /\u003e\n      \u003cValue Type=\"LookupMulti\"\u003eMartin\u003c/Value\u003e\n    \u003c/Eq\u003e\n  \u003c/And\u003e\n\u003c/Where\u003e\n```\n\nIf you want to know more about Camlex internals, check [Wiki documentation](https://github.com/sadomovalex/camlex/wiki).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsadomovalex%2Fcamlex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsadomovalex%2Fcamlex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsadomovalex%2Fcamlex/lists"}